Line data Source code
1 : #include "burp.h"
2 : #include "alloc.h"
3 : #include "asfd.h"
4 : #include "async.h"
5 : #include "cmd.h"
6 : #include "fsops.h"
7 : #include "handy.h"
8 : #include "iobuf.h"
9 : #include "log.h"
10 :
11 : // For IPTOS / IPTOS_THROUGHPUT.
12 : #ifdef HAVE_WIN32
13 : #include <ws2tcpip.h>
14 : #else
15 : #include <netinet/ip.h>
16 : #endif
17 :
18 : #ifdef HAVE_NCURSES_H
19 : #include "ncurses.h"
20 : #endif
21 :
22 : #include "protocol2/blist.h"
23 :
24 : static size_t bufmaxsize=(ASYNC_BUF_LEN*2)+32;
25 :
26 0 : static void truncate_readbuf(struct asfd *asfd)
27 : {
28 0 : asfd->readbuf[0]='\0';
29 0 : asfd->readbuflen=0;
30 0 : }
31 :
32 0 : static int asfd_alloc_buf(char **buf)
33 : {
34 0 : if(!*buf && !(*buf=(char *)calloc_w(1, bufmaxsize, __func__)))
35 0 : return -1;
36 0 : return 0;
37 : }
38 :
39 0 : static int extract_buf(struct asfd *asfd,
40 : unsigned int len, unsigned int offset)
41 : {
42 0 : if(!(asfd->rbuf->buf=(char *)malloc_w(len+1, __func__)))
43 0 : return -1;
44 0 : if(!(memcpy(asfd->rbuf->buf, asfd->readbuf+offset, len)))
45 : {
46 0 : logp("%s: memcpy failed in %s\n", asfd->desc, __func__);
47 0 : return -1;
48 : }
49 0 : asfd->rbuf->buf[len]='\0';
50 0 : if(!(memmove(asfd->readbuf,
51 0 : asfd->readbuf+len+offset, asfd->readbuflen-len-offset)))
52 : {
53 0 : logp("%s: memmove failed in %s\n", asfd->desc, __func__);
54 0 : return -1;
55 : }
56 0 : asfd->readbuflen-=len+offset;
57 0 : asfd->rbuf->len=len;
58 0 : return 0;
59 : }
60 :
61 : #ifdef HAVE_NCURSES_H
62 0 : static int parse_readbuf_ncurses(struct asfd *asfd)
63 : {
64 0 : if(!asfd->readbuflen) return 0;
65 : // This is reading ints, and will be cast back to an int when it comes
66 : // to be processed later.
67 0 : if(extract_buf(asfd, asfd->readbuflen, 0)) return -1;
68 0 : return 0;
69 : }
70 : #endif
71 :
72 0 : static int parse_readbuf_line_buf(struct asfd *asfd)
73 : {
74 : static char *cp=NULL;
75 : static char *dp=NULL;
76 : static size_t len=0;
77 0 : if(!cp)
78 : {
79 : // Only start from the beginning if we previously got something
80 : // to extract.
81 0 : cp=asfd->readbuf;
82 0 : len=0;
83 : }
84 0 : for(; len<asfd->readbuflen; cp++, len++)
85 : {
86 0 : if(*cp!='\n') continue;
87 0 : len++;
88 0 : if(extract_buf(asfd, len, 0)) return -1;
89 : // Strip trailing white space, like '\r\n'.
90 0 : dp=asfd->rbuf->buf;
91 0 : for(cp=&(dp[len-1]); cp>=dp && isspace(*cp); cp--, len--)
92 0 : *cp='\0';
93 0 : asfd->rbuf->len=len;
94 0 : break;
95 : }
96 0 : cp=NULL;
97 0 : return 0;
98 : }
99 :
100 0 : static int parse_readbuf_standard(struct asfd *asfd)
101 : {
102 0 : enum cmd cmdtmp=CMD_ERROR;
103 0 : unsigned int s=0;
104 0 : if(asfd->readbuflen<5) return 0;
105 0 : if((sscanf(asfd->readbuf, "%c%04X", (char *)&cmdtmp, &s))!=2)
106 : {
107 : logp("%s: sscanf of '%s' failed in %s\n",
108 0 : asfd->desc, asfd->readbuf, __func__);
109 0 : return -1;
110 : }
111 0 : if(asfd->readbuflen>=s+5)
112 : {
113 0 : asfd->rbuf->cmd=cmdtmp;
114 0 : if(extract_buf(asfd, s, 5))
115 0 : return -1;
116 : }
117 0 : return 0;
118 : }
119 :
120 0 : static int asfd_parse_readbuf(struct asfd *asfd)
121 : {
122 0 : if(asfd->rbuf->buf) return 0;
123 :
124 0 : if(asfd->parse_readbuf_specific(asfd))
125 : {
126 0 : truncate_readbuf(asfd);
127 0 : return -1;
128 : }
129 :
130 0 : return 0;
131 : }
132 :
133 : #ifdef HAVE_NCURSES_H
134 0 : static int asfd_do_read_ncurses(struct asfd *asfd)
135 : {
136 : static int i;
137 0 : i=getch();
138 0 : asfd->readbuflen=sizeof(int);
139 0 : memcpy(asfd->readbuf, &i, asfd->readbuflen);
140 0 : return 0;
141 : }
142 :
143 0 : static int asfd_do_write_ncurses(struct asfd *asfd)
144 : {
145 0 : logp("This function should not have been called: %s\n", __func__);
146 0 : return -1;
147 : }
148 : #endif
149 :
150 0 : static int asfd_do_read(struct asfd *asfd)
151 : {
152 : ssize_t r;
153 : r=read(asfd->fd,
154 0 : asfd->readbuf+asfd->readbuflen, bufmaxsize-asfd->readbuflen);
155 0 : if(r<0)
156 : {
157 0 : if(errno==EAGAIN || errno==EINTR)
158 0 : return 0;
159 : logp("%s: read problem on fd %d: %s\n",
160 0 : asfd->desc, asfd->fd, strerror(errno));
161 0 : goto error;
162 : }
163 0 : else if(!r)
164 : {
165 : // End of data.
166 0 : logp("%s: end of data\n", asfd->desc);
167 0 : goto error;
168 : }
169 0 : asfd->readbuflen+=r;
170 0 : return 0;
171 : error:
172 0 : truncate_readbuf(asfd);
173 0 : return -1;
174 : }
175 :
176 0 : static int asfd_do_read_ssl(struct asfd *asfd)
177 : {
178 : int e;
179 : ssize_t r;
180 :
181 0 : asfd->read_blocked_on_write=0;
182 :
183 0 : ERR_clear_error();
184 : r=SSL_read(asfd->ssl,
185 0 : asfd->readbuf+asfd->readbuflen, bufmaxsize-asfd->readbuflen);
186 :
187 0 : switch((e=SSL_get_error(asfd->ssl, r)))
188 : {
189 : case SSL_ERROR_NONE:
190 0 : asfd->readbuflen+=r;
191 0 : asfd->readbuf[asfd->readbuflen]='\0';
192 0 : break;
193 : case SSL_ERROR_ZERO_RETURN:
194 : // End of data.
195 0 : logp("%s: Peer closed SSL session\n", asfd->desc);
196 0 : SSL_shutdown(asfd->ssl);
197 0 : goto error;
198 : case SSL_ERROR_WANT_READ:
199 0 : break;
200 : case SSL_ERROR_WANT_WRITE:
201 0 : asfd->read_blocked_on_write=1;
202 0 : break;
203 : case SSL_ERROR_SYSCALL:
204 0 : if(errno==EAGAIN || errno==EINTR)
205 : break;
206 : logp("%s: Got SSL_ERROR_SYSCALL\n",
207 0 : asfd->desc);
208 : // Fall through to read problem
209 : default:
210 : logp_ssl_err(
211 : "%s: SSL read problem in %s: %d - %d=%s\n",
212 : asfd->desc, __func__,
213 0 : e, errno, strerror(errno));
214 0 : goto error;
215 : }
216 0 : return 0;
217 : error:
218 0 : truncate_readbuf(asfd);
219 0 : return -1;
220 : }
221 :
222 : // Return 0 for OK to write, non-zero for not OK to write.
223 0 : static int check_ratelimit(struct asfd *asfd)
224 : {
225 : float f;
226 : time_t diff;
227 0 : if(!asfd->rlstart) asfd->rlstart=time(NULL);
228 0 : if((diff=asfd->as->now-asfd->rlstart)<0)
229 : {
230 : // It is possible that the clock changed. Reset ourselves.
231 0 : asfd->as->now=asfd->rlstart;
232 0 : asfd->rlbytes=0;
233 : logp("Looks like the clock went back in time since starting. "
234 0 : "Resetting ratelimit\n");
235 0 : return 0;
236 : }
237 0 : if(!diff) return 0; // Need to get started somehow.
238 0 : f=(asfd->rlbytes)/diff; // Bytes per second.
239 :
240 0 : if(f>=asfd->ratelimit)
241 : {
242 : #ifdef HAVE_WIN32
243 : // Windows Sleep is milliseconds, usleep is microseconds.
244 : // Do some conversion.
245 : Sleep(asfd->rlsleeptime/1000);
246 : #else
247 0 : usleep(asfd->rlsleeptime);
248 : #endif
249 : // If sleeping, increase the sleep time.
250 0 : if((asfd->rlsleeptime*=2)>=500000) asfd->rlsleeptime=500000;
251 0 : return 1;
252 : }
253 : // If not sleeping, decrease the sleep time.
254 0 : if((asfd->rlsleeptime/=2)<=9999) asfd->rlsleeptime=10000;
255 0 : return 0;
256 : }
257 :
258 0 : static int asfd_do_write(struct asfd *asfd)
259 : {
260 : ssize_t w;
261 0 : if(asfd->ratelimit && check_ratelimit(asfd)) return 0;
262 :
263 0 : w=write(asfd->fd, asfd->writebuf, asfd->writebuflen);
264 0 : if(w<0)
265 : {
266 0 : if(errno==EAGAIN || errno==EINTR)
267 0 : return 0;
268 : logp("%s: Got error in %s, (%d=%s)\n", __func__,
269 0 : asfd->desc, errno, strerror(errno));
270 0 : return -1;
271 : }
272 0 : else if(!w)
273 : {
274 0 : logp("%s: Wrote nothing in %s\n", asfd->desc, __func__);
275 0 : return -1;
276 : }
277 0 : if(asfd->ratelimit) asfd->rlbytes+=w;
278 : /*
279 : {
280 : char buf[100000]="";
281 : snprintf(buf, w+1, "%s", asfd->writebuf);
282 : printf("wrote %d: %s\n", w, buf);
283 : }
284 : */
285 :
286 0 : memmove(asfd->writebuf, asfd->writebuf+w, asfd->writebuflen-w);
287 0 : asfd->writebuflen-=w;
288 0 : return 0;
289 : }
290 :
291 0 : static int asfd_do_write_ssl(struct asfd *asfd)
292 : {
293 : int e;
294 : ssize_t w;
295 :
296 0 : asfd->write_blocked_on_read=0;
297 :
298 0 : if(asfd->ratelimit && check_ratelimit(asfd)) return 0;
299 0 : ERR_clear_error();
300 0 : w=SSL_write(asfd->ssl, asfd->writebuf, asfd->writebuflen);
301 :
302 0 : switch((e=SSL_get_error(asfd->ssl, w)))
303 : {
304 : case SSL_ERROR_NONE:
305 : /*
306 : {
307 : char buf[100000]="";
308 : snprintf(buf, w+1, "%s", asfd->writebuf);
309 : printf("wrote %d: %s\n", w, buf);
310 : }
311 : */
312 0 : if(asfd->ratelimit) asfd->rlbytes+=w;
313 : memmove(asfd->writebuf,
314 0 : asfd->writebuf+w, asfd->writebuflen-w);
315 0 : asfd->writebuflen-=w;
316 0 : break;
317 : case SSL_ERROR_WANT_WRITE:
318 0 : break;
319 : case SSL_ERROR_WANT_READ:
320 0 : asfd->write_blocked_on_read=1;
321 0 : break;
322 : case SSL_ERROR_SYSCALL:
323 0 : if(errno==EAGAIN || errno==EINTR)
324 : break;
325 : logp("%s: Got SSL_ERROR_SYSCALL\n",
326 0 : asfd->desc);
327 : // Fall through to read problem
328 : default:
329 : logp_ssl_err(
330 : "%s: SSL write problem in %s: %d - %d=%s\n",
331 : asfd->desc, __func__,
332 0 : e, errno, strerror(errno));
333 0 : return -1;
334 : }
335 0 : return 0;
336 : }
337 :
338 0 : static int append_to_write_buffer(struct asfd *asfd,
339 : const char *buf, size_t len)
340 : {
341 0 : memcpy(asfd->writebuf+asfd->writebuflen, buf, len);
342 0 : asfd->writebuflen+=len;
343 0 : asfd->writebuf[asfd->writebuflen]='\0';
344 0 : return 0;
345 : }
346 :
347 0 : static enum append_ret asfd_append_all_to_write_buffer(struct asfd *asfd,
348 : struct iobuf *wbuf)
349 : {
350 0 : switch(asfd->streamtype)
351 : {
352 : case ASFD_STREAM_STANDARD:
353 : {
354 0 : size_t sblen=0;
355 0 : char sbuf[10]="";
356 0 : if(asfd->writebuflen+6+(wbuf->len) >= bufmaxsize-1)
357 0 : return APPEND_BLOCKED;
358 :
359 : snprintf(sbuf, sizeof(sbuf), "%c%04X",
360 0 : wbuf->cmd, (unsigned int)wbuf->len);
361 0 : sblen=strlen(sbuf);
362 0 : append_to_write_buffer(asfd, sbuf, sblen);
363 0 : break;
364 : }
365 : case ASFD_STREAM_LINEBUF:
366 0 : if(asfd->writebuflen+wbuf->len >= bufmaxsize-1)
367 0 : return APPEND_BLOCKED;
368 0 : break;
369 : case ASFD_STREAM_NCURSES_STDIN:
370 : default:
371 : logp("%s: unknown asfd stream type in %s: %d\n",
372 0 : asfd->desc, __func__, asfd->streamtype);
373 0 : return APPEND_ERROR;
374 : }
375 0 : append_to_write_buffer(asfd, wbuf->buf, wbuf->len);
376 : //printf("append %d: %c:%s\n", wbuf->len, wbuf->cmd, wbuf->buf);
377 0 : wbuf->len=0;
378 0 : return APPEND_OK;
379 : }
380 :
381 0 : static int asfd_set_bulk_packets(struct asfd *asfd)
382 : {
383 : #ifdef IP_TOS
384 : #ifndef IPTOS_THROUGHPUT
385 : // Windows/mingw64 does not define this, but it is just a bit in the packet
386 : // header. Set it ourselves. According to what I have read on forums, the
387 : // Windows machine may have some system wide policy that resets the bits.
388 : // At least the burp code will be doing the right thing by setting it, even
389 : // if Windows decides to remove it.
390 : #define IPTOS_THROUGHPUT 0x08
391 : #endif
392 0 : int opt=IPTOS_THROUGHPUT;
393 0 : if(asfd->fd<0) return -1;
394 0 : if(setsockopt(asfd->fd,
395 0 : IPPROTO_IP, IP_TOS, (char *)&opt, sizeof(opt))<0)
396 : {
397 : logp("%s: error: setsockopt IPTOS_THROUGHPUT: %s\n",
398 0 : asfd->desc, strerror(errno));
399 0 : return -1;
400 : }
401 : #endif
402 0 : return 0;
403 : }
404 :
405 0 : static int asfd_read(struct asfd *asfd)
406 : {
407 0 : if(asfd->as->doing_estimate) return 0;
408 0 : while(!asfd->rbuf->buf)
409 0 : if(asfd->as->read_write(asfd->as)) return -1;
410 0 : return 0;
411 : }
412 :
413 0 : static int asfd_read_expect(struct asfd *asfd, enum cmd cmd, const char *expect)
414 : {
415 0 : int ret=0;
416 0 : if(asfd->read(asfd)) return -1;
417 0 : if(asfd->rbuf->cmd!=cmd || strcmp(asfd->rbuf->buf, expect))
418 : {
419 : logp("%s: expected '%c:%s', got '%c:%s'\n",
420 : asfd->desc, cmd, expect,
421 0 : asfd->rbuf->cmd, asfd->rbuf->buf);
422 0 : ret=-1;
423 : }
424 0 : iobuf_free_content(asfd->rbuf);
425 0 : return ret;
426 : }
427 :
428 0 : static int asfd_write(struct asfd *asfd, struct iobuf *wbuf)
429 : {
430 0 : if(asfd->as->doing_estimate) return 0;
431 0 : while(wbuf->len)
432 : {
433 0 : if(asfd->append_all_to_write_buffer(asfd, wbuf)==APPEND_ERROR)
434 0 : return -1;
435 0 : if(asfd->as->write(asfd->as)) return -1;
436 : }
437 0 : return 0;
438 : }
439 :
440 0 : static int asfd_write_strn(struct asfd *asfd,
441 : enum cmd wcmd, const char *wsrc, size_t len)
442 : {
443 : struct iobuf wbuf;
444 0 : wbuf.cmd=wcmd;
445 0 : wbuf.buf=(char *)wsrc;
446 0 : wbuf.len=len;
447 0 : return asfd->write(asfd, &wbuf);
448 : }
449 :
450 0 : static int asfd_write_str(struct asfd *asfd, enum cmd wcmd, const char *wsrc)
451 : {
452 0 : return asfd_write_strn(asfd, wcmd, wsrc, strlen(wsrc));
453 : }
454 :
455 0 : static int asfd_simple_loop(struct asfd *asfd,
456 : struct conf **confs, void *param, const char *caller,
457 : enum asl_ret callback(struct asfd *asfd, struct conf **confs, void *param))
458 : {
459 0 : struct iobuf *rbuf=asfd->rbuf;
460 : while(1)
461 : {
462 0 : iobuf_free_content(rbuf);
463 0 : if(asfd->read(asfd)) goto error;
464 0 : if(rbuf->cmd!=CMD_GEN)
465 : {
466 0 : if(rbuf->cmd==CMD_WARNING
467 0 : || rbuf->cmd==CMD_MESSAGE)
468 : {
469 0 : struct cntr *cntr=NULL;
470 0 : if(confs) cntr=get_cntr(confs);
471 0 : log_recvd(rbuf, cntr, 0);
472 : }
473 0 : else if(rbuf->cmd==CMD_INTERRUPT)
474 : {
475 : // Ignore - client wanted to interrupt a file.
476 : }
477 : else
478 : {
479 0 : logp("%s: unexpected command in %s(), called from %s(): %c:%s\n", asfd->desc, __func__, caller, rbuf->cmd, rbuf->buf);
480 0 : goto error;
481 : }
482 0 : continue;
483 : }
484 0 : switch(callback(asfd, confs, param))
485 : {
486 0 : case ASL_CONTINUE: break;
487 : case ASL_END_OK:
488 0 : iobuf_free_content(rbuf);
489 0 : return 0;
490 : case ASL_END_OK_RETURN_1:
491 0 : iobuf_free_content(rbuf);
492 0 : return 1;
493 : case ASL_END_ERROR:
494 : default:
495 0 : goto error;
496 : }
497 : }
498 : error:
499 0 : iobuf_free_content(rbuf);
500 0 : return -1;
501 : }
502 :
503 :
504 0 : static int asfd_init(struct asfd *asfd, const char *desc,
505 : struct async *as, int afd, SSL *assl,
506 : enum asfd_streamtype streamtype, struct conf **confs)
507 : {
508 0 : asfd->as=as;
509 0 : asfd->fd=afd;
510 0 : asfd->ssl=assl;
511 0 : asfd->streamtype=streamtype;
512 0 : asfd->max_network_timeout=get_int(confs[OPT_NETWORK_TIMEOUT]);
513 0 : asfd->network_timeout=asfd->max_network_timeout;
514 0 : asfd->ratelimit=get_float(confs[OPT_RATELIMIT]);
515 0 : asfd->rlsleeptime=10000;
516 0 : asfd->pid=-1;
517 :
518 0 : asfd->parse_readbuf=asfd_parse_readbuf;
519 0 : asfd->append_all_to_write_buffer=asfd_append_all_to_write_buffer;
520 0 : asfd->set_bulk_packets=asfd_set_bulk_packets;
521 0 : if(asfd->ssl)
522 : {
523 0 : asfd->do_read=asfd_do_read_ssl;
524 0 : asfd->do_write=asfd_do_write_ssl;
525 : }
526 : else
527 : {
528 0 : asfd->do_read=asfd_do_read;
529 0 : asfd->do_write=asfd_do_write;
530 : #ifdef HAVE_NCURSES_H
531 0 : if(asfd->streamtype==ASFD_STREAM_NCURSES_STDIN)
532 : {
533 0 : asfd->do_read=asfd_do_read_ncurses;
534 0 : asfd->do_write=asfd_do_write_ncurses;
535 : }
536 : #endif
537 : }
538 0 : asfd->read=asfd_read;
539 0 : asfd->read_expect=asfd_read_expect;
540 0 : asfd->simple_loop=asfd_simple_loop;
541 0 : asfd->write=asfd_write;
542 0 : asfd->write_str=asfd_write_str;
543 0 : asfd->write_strn=asfd_write_strn;
544 :
545 0 : switch(asfd->streamtype)
546 : {
547 : case ASFD_STREAM_STANDARD:
548 0 : asfd->parse_readbuf_specific=parse_readbuf_standard;
549 0 : break;
550 : case ASFD_STREAM_LINEBUF:
551 0 : asfd->parse_readbuf_specific=parse_readbuf_line_buf;
552 0 : break;
553 : #ifdef HAVE_NCURSES_H
554 : case ASFD_STREAM_NCURSES_STDIN:
555 0 : asfd->parse_readbuf_specific=parse_readbuf_ncurses;
556 0 : break;
557 : #endif
558 : default:
559 : logp("%s: unknown asfd stream type in %s: %d\n",
560 0 : desc, __func__, asfd->streamtype);
561 0 : return -1;
562 : }
563 :
564 0 : if(!(asfd->rbuf=iobuf_alloc())
565 0 : || asfd_alloc_buf(&asfd->readbuf)
566 0 : || asfd_alloc_buf(&asfd->writebuf)
567 0 : || !(asfd->desc=strdup_w(desc, __func__)))
568 0 : return -1;
569 0 : return 0;
570 : }
571 :
572 11 : struct asfd *asfd_alloc(void)
573 : {
574 : struct asfd *asfd;
575 11 : if(!(asfd=(struct asfd *)calloc_w(1, sizeof(struct asfd), __func__)))
576 0 : return NULL;
577 11 : asfd->init=asfd_init;
578 11 : return asfd;
579 : }
580 :
581 11 : void asfd_close(struct asfd *asfd)
582 : {
583 22 : if(!asfd) return;
584 11 : if(asfd->ssl && asfd->fd>=0)
585 : {
586 : int r;
587 0 : set_blocking(asfd->fd);
588 : // I do not think this SSL_shutdown stuff works right.
589 : // Ignore it for now.
590 : #ifndef HAVE_WIN32
591 0 : signal(SIGPIPE, SIG_IGN);
592 : #endif
593 0 : if(!(r=SSL_shutdown(asfd->ssl)))
594 : {
595 0 : shutdown(asfd->fd, 1);
596 0 : r=SSL_shutdown(asfd->ssl);
597 : }
598 : }
599 11 : if(asfd->ssl)
600 : {
601 0 : SSL_free(asfd->ssl);
602 0 : asfd->ssl=NULL;
603 : }
604 11 : close_fd(&asfd->fd);
605 : }
606 :
607 11 : void asfd_free(struct asfd **asfd)
608 : {
609 22 : if(!asfd || !*asfd) return;
610 11 : asfd_close(*asfd);
611 11 : iobuf_free(&((*asfd)->rbuf));
612 11 : free_w(&((*asfd)->readbuf));
613 11 : free_w(&((*asfd)->writebuf));
614 11 : free_w(&((*asfd)->desc));
615 : // FIX THIS: free incoming?
616 11 : blist_free(&((*asfd)->blist));
617 11 : free_v((void **)asfd);
618 : }
619 :
620 0 : struct asfd *setup_asfd(struct async *as, const char *desc, int *fd, SSL *ssl,
621 : enum asfd_streamtype asfd_streamtype, enum asfd_fdtype fdtype,
622 : pid_t pid, struct conf **conf)
623 : {
624 0 : struct asfd *asfd=NULL;
625 0 : if(!fd || *fd<0)
626 : {
627 0 : logp("Given invalid descriptor in %s\n", __func__);
628 0 : goto error;
629 : }
630 0 : set_non_blocking(*fd);
631 0 : if(!(asfd=asfd_alloc())
632 0 : || asfd->init(asfd, desc, as, *fd, ssl, asfd_streamtype, conf))
633 0 : goto error;
634 0 : asfd->fdtype=fdtype;
635 0 : asfd->pid=pid;
636 0 : *fd=-1;
637 0 : as->asfd_add(as, asfd);
638 0 : return asfd;
639 : error:
640 0 : asfd_free(&asfd);
641 0 : return NULL;
642 : }
|