1 /*
2 * binkleyforce -- unix FTN mailer project
3 *
4 * Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * $Id: prot_binkp.c,v 1.1.1.1 2004/09/09 09:52:38 kstepanenkov Exp $
12 */
13
14 #include "includes.h"
15 #include "confread.h"
16 #include "logger.h"
17 #include "util.h"
18 #include "io.h"
19 #include "session.h"
20 #include "outbound.h"
21 #include "prot_common.h"
22 #include "prot_binkp.h"
23
24 typedef enum {
25 BPI_SendSysInfo,
26 BPI_WaitADR,
27 BPI_WaitPWD,
28 BPI_Auth
29 } binkp_incoming_state;
30
31 typedef enum {
32 BPO_SendSysInfo,
33 BPO_WaitNUL,
34 BPO_SendPWD,
35 BPO_WaitADR,
36 BPO_Auth,
37 BPO_WaitOK
38 } binkp_outgoing_state;
39
40 #define GOTO(label,newrc) { rc = (newrc); goto label; }
41
binkp_process_NUL(s_binkp_sysinfo * remote_data,char * buffer)42 void binkp_process_NUL(s_binkp_sysinfo *remote_data, char *buffer)
43 {
44 char *p, *q;
45
46 if( strncmp(buffer, "SYS ", 4) == 0 )
47 strnxcpy(remote_data->systname, buffer+4, sizeof(remote_data->systname));
48 else if( strncmp(buffer, "ZYZ ", 4) == 0 )
49 strnxcpy(remote_data->sysop, buffer+4, sizeof(remote_data->sysop));
50 else if( strncmp(buffer, "LOC ", 4) == 0 )
51 strnxcpy(remote_data->location, buffer+4, sizeof(remote_data->location));
52 else if( strncmp(buffer, "PHN ", 4) == 0 )
53 strnxcpy(remote_data->phone, buffer+4, sizeof(remote_data->phone));
54 else if( strncmp(buffer, "NDL ", 4) == 0 )
55 strnxcpy(remote_data->flags, buffer+4, sizeof(remote_data->flags));
56 else if( strncmp(buffer, "TIME ", 5) == 0 )
57 strnxcpy(remote_data->timestr, buffer+5, sizeof(remote_data->timestr));
58 else if( strncmp(buffer, "OPT ", 4) == 0 )
59 {
60 if( *remote_data->opt )
61 {
62 strnxcat(remote_data->opt, " ", sizeof(remote_data->opt));
63 strnxcat(remote_data->opt, buffer+4, sizeof(remote_data->opt));
64 }
65 else
66 strnxcpy(remote_data->opt, buffer+4, sizeof(remote_data->opt));
67
68 binkp_parse_options(remote_data, buffer+4);
69 }
70 else if( strncmp(buffer, "VER ", 4) == 0 )
71 {
72 /* <mailer> [<protocol>/<vermaj>.<vermin>] */
73 if( (p = strchr(buffer+4, ' ')) )
74 {
75 strnxcpy(remote_data->progname, buffer+4,
76 MIN(sizeof(remote_data->progname), p - (buffer+4) + 1));
77 ++p;
78 if( (q = strchr(p, '/')) )
79 {
80 strnxcpy(remote_data->protname, p,
81 MIN(sizeof(remote_data->protname), q - p + 1));
82 sscanf(q+1, "%d.%d",
83 &remote_data->majorver,
84 &remote_data->minorver);
85 }
86 }
87 else
88 strnxcpy(remote_data->progname, buffer+4, sizeof(remote_data->progname));
89 }
90 else
91 bf_log("BinkP got invalid NUL: \"%s\"", string_printable(buffer));
92 }
93
binkp_process_ADR(s_binkp_sysinfo * remote_data,char * buffer)94 void binkp_process_ADR(s_binkp_sysinfo *remote_data, char *buffer)
95 {
96 s_faddr addr;
97 char *p, *q;
98
99 for( p = string_token(buffer, &q, NULL, 0); p;
100 p = string_token(NULL, &q, NULL, 0) )
101 {
102 if( ftn_addrparse(&addr, p, FALSE) )
103 bf_log("BinkP got unparsable address \"%s\"", string_printable(p));
104 else
105 session_addrs_add(&remote_data->addrs, &remote_data->anum, addr);
106 }
107 }
108
binkp_outgoing(s_binkp_sysinfo * local_data,s_binkp_sysinfo * remote_data)109 int binkp_outgoing(s_binkp_sysinfo *local_data, s_binkp_sysinfo *remote_data)
110 {
111 s_bpinfo bpi;
112 binkp_outgoing_state binkp_state = BPO_SendSysInfo;
113 int rc = HRC_OK;
114 int recv_rc = 0;
115 int send_rc = 0;
116 bool send_ready = FALSE;
117 bool recv_ready = FALSE;
118
119 binkp_init_bpinfo(&bpi);
120
121 while(1)
122 {
123 switch(binkp_state) {
124 case BPO_SendSysInfo:
125 binkp_queue_sysinfo(&bpi, local_data);
126 binkp_state = BPO_WaitNUL;
127 break;
128
129 case BPO_SendPWD:
130 if( *local_data->passwd == '\0' )
131 {
132 binkp_queuemsg(&bpi, BPMSG_PWD, NULL, "-");
133 }
134 else if( remote_data->options & BINKP_OPT_MD5 )
135 {
136 char digest_bin[16];
137 char digest_hex[33];
138
139 md5_cram_get(local_data->passwd, remote_data->challenge,
140 remote_data->challenge_length, digest_bin);
141
142 /* Encode digest to the hex string */
143 string_bin_to_hex(digest_hex, digest_bin, 16);
144
145 binkp_queuemsg(&bpi, BPMSG_PWD, "CRAM-MD5-", digest_hex);
146 }
147 else
148 binkp_queuemsg(&bpi, BPMSG_PWD, NULL, local_data->passwd);
149
150 binkp_state = BPO_WaitADR;
151 break;
152
153 case BPO_Auth:
154 /* Set remote password same as local */
155 strncpy(remote_data->passwd, local_data->passwd, BINKP_MAXPASSWD);
156 remote_data->passwd[BINKP_MAXPASSWD] = '\0';
157
158 if( !remote_data->anum )
159 {
160 binkp_queuemsg(&bpi, BPMSG_BSY, NULL, "No addresses was presented");
161 GOTO(Exit, HRC_BUSY);
162 }
163 else if( session_addrs_check_genuine(remote_data->addrs, remote_data->anum,
164 state.node.addr) )
165 {
166 binkp_queuemsg(&bpi, BPMSG_ERR, NULL, "Sorry, you are not who I need");
167 GOTO(Exit, HRC_NO_ADDRESS);
168 }
169 else if( session_addrs_check(remote_data->addrs, remote_data->anum,
170 remote_data->passwd, NULL, 0) )
171 {
172 binkp_queuemsg(&bpi, BPMSG_ERR, NULL, "Security violation");
173 GOTO(Exit, HRC_BAD_PASSWD);
174 }
175 else if( session_addrs_lock(remote_data->addrs, remote_data->anum) )
176 {
177 binkp_queuemsg(&bpi, BPMSG_BSY, NULL, "All addresses are busy");
178 GOTO(Exit, HRC_BUSY);
179 }
180 binkp_state = BPO_WaitOK;
181 break;
182
183 default:
184 break;
185 }
186
187 /*
188 * Receive/Send next data block
189 */
190 send_ready = recv_ready = FALSE;
191
192 if( tty_select(&recv_ready, (bpi.opos || bpi.n_msgs) ?
193 &send_ready : NULL, bpi.timeout) < 0 )
194 GOTO(Abort, HRC_OTHER_ERR);
195
196 recv_rc = BPMSG_NONE;
197 send_rc = 0;
198
199 if( recv_ready && (recv_rc = binkp_recv(&bpi)) == BPMSG_EXIT )
200 GOTO(Abort, HRC_OTHER_ERR);
201
202 if( send_ready && (send_rc = binkp_send(&bpi)) < 0 )
203 GOTO(Abort, HRC_OTHER_ERR);
204
205 /*
206 * Handle received message
207 */
208 switch(recv_rc) {
209 case BPMSG_NONE:
210 break;
211
212 case BPMSG_NUL:
213 if( binkp_state == BPO_WaitNUL || binkp_state == BPO_WaitADR )
214 {
215 binkp_process_NUL(remote_data, bpi.ibuf+1);
216 if( binkp_state == BPO_WaitNUL )
217 binkp_state = BPO_SendPWD;
218 }
219 break;
220
221 case BPMSG_ADR:
222 if( binkp_state == BPO_WaitADR )
223 {
224 binkp_process_ADR(remote_data, bpi.ibuf+1);
225 binkp_state = BPO_Auth;
226 }
227 break;
228
229 case BPMSG_OK:
230 if( binkp_state == BPO_WaitOK )
231 GOTO(Exit, HRC_OK);
232 break;
233
234 case BPMSG_ERR:
235 bf_log("BinkP error: \"%s\"", string_printable(bpi.ibuf+1));
236 GOTO(Abort, HRC_FATAL_ERR);
237
238 case BPMSG_BSY:
239 bf_log("BinkP busy: \"%s\"", string_printable(bpi.ibuf+1));
240 GOTO(Abort, HRC_TEMP_ERR);
241 }
242 }
243
244 Exit:
245 if( binkp_flush_queue(&bpi, bpi.timeout) && rc == HRC_OK )
246 rc = HRC_OTHER_ERR;
247
248 Abort:
249 binkp_deinit_bpinfo(&bpi);
250
251 return rc;
252 }
253
254
binkp_auth_incoming(s_binkp_sysinfo * remote_data)255 int binkp_auth_incoming(s_binkp_sysinfo *remote_data)
256 {
257 if( remote_data->challenge_length > 0
258 && strncmp(remote_data->passwd, "CRAM-MD5-", 9) == 0 )
259 {
260 return session_addrs_check(remote_data->addrs,
261 remote_data->anum,
262 remote_data->passwd + 9,
263 remote_data->challenge,
264 remote_data->challenge_length);
265 }
266
267 return session_addrs_check(remote_data->addrs, remote_data->anum,
268 remote_data->passwd, NULL, 0);
269 }
270
binkp_incoming(s_binkp_sysinfo * local_data,s_binkp_sysinfo * remote_data)271 int binkp_incoming(s_binkp_sysinfo *local_data, s_binkp_sysinfo *remote_data)
272 {
273 s_bpinfo bpi;
274 binkp_incoming_state binkp_state = BPI_SendSysInfo;
275 int rc = HRC_OK;
276 int recv_rc = 0;
277 int send_rc = 0;
278 bool send_ready = FALSE;
279 bool recv_ready = FALSE;
280
281 binkp_init_bpinfo(&bpi);
282
283 while(1)
284 {
285 switch(binkp_state) {
286 case BPI_SendSysInfo:
287 binkp_queue_sysinfo(&bpi, local_data);
288 binkp_state = BPI_WaitADR;
289 break;
290
291 case BPI_Auth:
292 /* Set challenge string same as local */
293 memcpy(remote_data->challenge, local_data->challenge,
294 sizeof(remote_data->challenge));
295 remote_data->challenge_length = local_data->challenge_length;
296
297 /* Do authorization */
298 if( !remote_data->anum )
299 {
300 binkp_queuemsg(&bpi, BPMSG_BSY, NULL, "No addresses was presented");
301 GOTO(Exit, HRC_BUSY);
302 }
303 else if( binkp_auth_incoming(remote_data) )
304 {
305 binkp_queuemsg(&bpi, BPMSG_ERR, NULL, "Security violation");
306 GOTO(Exit, HRC_BAD_PASSWD);
307 }
308 else if( session_addrs_lock(remote_data->addrs, remote_data->anum) )
309 {
310 binkp_queuemsg(&bpi, BPMSG_BSY, NULL, "All addresses are busy");
311 GOTO(Exit, HRC_BUSY);
312 }
313 else
314 {
315 binkp_queuemsg(&bpi, BPMSG_OK, NULL, NULL);
316 GOTO(Exit, HRC_OK);
317 }
318 break;
319
320 default:
321 break;
322 }
323
324 /*
325 * Receive/Send next data block
326 */
327 send_ready = recv_ready = FALSE;
328
329 if( tty_select(&recv_ready, (bpi.opos || bpi.n_msgs) ?
330 &send_ready : NULL, bpi.timeout) < 0 )
331 GOTO(Abort, HRC_OTHER_ERR);
332
333 recv_rc = BPMSG_NONE;
334 send_rc = 0;
335
336 if( recv_ready && (recv_rc = binkp_recv(&bpi)) == BPMSG_EXIT )
337 GOTO(Abort, HRC_OTHER_ERR);
338
339 if( send_ready && (send_rc = binkp_send(&bpi)) < 0 )
340 GOTO(Abort, HRC_OTHER_ERR);
341
342 /*
343 * Handle received message
344 */
345 switch(recv_rc) {
346 case BPMSG_NONE:
347 break;
348
349 case BPMSG_NUL:
350 if( binkp_state == BPI_WaitADR )
351 binkp_process_NUL(remote_data, bpi.ibuf+1);
352 break;
353
354 case BPMSG_ADR:
355 if( binkp_state == BPI_WaitADR )
356 {
357 binkp_process_ADR(remote_data, bpi.ibuf+1);
358 binkp_state = BPI_WaitPWD;
359 }
360 break;
361
362 case BPMSG_PWD:
363 if( binkp_state == BPI_WaitPWD )
364 {
365 strnxcpy(remote_data->passwd, bpi.ibuf+1, sizeof(remote_data->passwd));
366 binkp_state = BPI_Auth;
367 }
368 break;
369
370 case BPMSG_ERR:
371 bf_log("BinkP error: \"%s\"", string_printable(bpi.ibuf+1));
372 GOTO(Abort, HRC_FATAL_ERR);
373
374 case BPMSG_BSY:
375 bf_log("BinkP busy: \"%s\"", string_printable(bpi.ibuf+1));
376 GOTO(Abort, HRC_TEMP_ERR);
377 }
378 }
379
380 Exit:
381 if( binkp_flush_queue(&bpi, bpi.timeout) && rc == HRC_OK )
382 rc = HRC_OTHER_ERR;
383
384 Abort:
385 binkp_deinit_bpinfo(&bpi);
386
387 return rc;
388 }
389
binkp_transfer(s_protinfo * pi)390 int binkp_transfer(s_protinfo *pi)
391 {
392 int i, n, rc = PRC_NOERROR;
393 bool recv_ready = FALSE;
394 bool send_ready = FALSE;
395 bool sent_EOB = FALSE;
396 bool rcvd_EOB = FALSE;
397 bool send_file = FALSE;
398 bool recv_file = FALSE;
399 bool wait_got = FALSE;
400 bool nofiles = FALSE;
401 int recv_rc = 0;
402 int send_rc = 0;
403 char *fname = NULL;
404 size_t fsize = 0;
405 time_t ftime = 0;
406 size_t foffs = 0;
407 s_bpinfo bpi;
408
409 binkp_init_bpinfo(&bpi);
410
411 while( !sent_EOB || !rcvd_EOB )
412 {
413 if( !send_file && !sent_EOB && bpi.opos == 0 && bpi.n_msgs == 0 )
414 {
415 if( !p_tx_fopen(pi) )
416 {
417 send_file = TRUE;
418 binkp_queuemsgf(&bpi, BPMSG_FILE, "%s %ld %ld 0",
419 pi->send->net_name, (long)pi->send->bytes_total,
420 (long)pi->send->mod_time);
421 }
422 else
423 /* No more files */
424 nofiles = TRUE;
425 }
426
427 if( send_file && bpi.opos == 0 && bpi.n_msgs == 0 )
428 {
429 if( (n = p_tx_readfile(bpi.obuf + BINKP_BLK_HDRSIZE,
430 4096, pi)) < 0 )
431 {
432 p_tx_fclose(pi);
433 send_file = FALSE;
434 }
435 else
436 {
437 binkp_puthdr(bpi.obuf, (unsigned)(n & 0x7fff));
438 bpi.opos = n + BINKP_BLK_HDRSIZE;
439 pi->send->bytes_sent += n;
440 if( pi->send->eofseen )
441 {
442 wait_got = TRUE; send_file = FALSE;
443 pi->send->status = FSTAT_WAITACK;
444 }
445 }
446 }
447
448 if( nofiles && !wait_got && !sent_EOB )
449 {
450 sent_EOB = TRUE;
451 binkp_queuemsg(&bpi, BPMSG_EOB, NULL, NULL);
452 }
453
454 recv_ready = send_ready = FALSE;
455 if( tty_select(&recv_ready, (bpi.opos || bpi.n_msgs) ?
456 &send_ready : NULL, bpi.timeout) < 0 )
457 gotoexit(PRC_ERROR);
458
459 recv_rc = BPMSG_NONE;
460 send_rc = 0;
461
462 if( recv_ready && (recv_rc = binkp_recv(&bpi)) == BPMSG_EXIT )
463 gotoexit(PRC_ERROR);
464 if( send_ready && (send_rc = binkp_send(&bpi)) < 0 )
465 gotoexit(PRC_ERROR);
466
467 switch(recv_rc) {
468 case BPMSG_NONE:
469 break;
470 case BPMSG_DATA: /* Got new data block */
471 if( recv_file )
472 {
473 if( (n = p_rx_writefile(bpi.ibuf, bpi.isize, pi)) < 0 )
474 {
475 /* error writing file */
476 if( n == -2 )
477 {
478 binkp_queuemsgf(&bpi, BPMSG_GOT, "%s %ld %ld",
479 pi->recv->net_name, (long)pi->recv->bytes_total,
480 (long)pi->recv->mod_time);
481 }
482 else
483 {
484 binkp_queuemsgf(&bpi, BPMSG_SKIP, "%s %ld %ld",
485 pi->recv->net_name, (long)pi->recv->bytes_total,
486 (long)pi->recv->mod_time);
487 }
488 recv_file = FALSE;
489 p_rx_fclose(pi);
490 }
491 else
492 {
493 pi->recv->bytes_received += bpi.isize;
494
495 /* Was it the last data block? */
496 if( pi->recv->bytes_received > pi->recv->bytes_total )
497 {
498 bf_log("binkp got too many data (%ld, %ld expected)",
499 (long)pi->recv->bytes_received,
500 (long)pi->recv->bytes_total);
501
502 recv_file = FALSE;
503 pi->recv->status = FSTAT_REFUSED;
504 (void)p_rx_fclose(pi);
505
506 binkp_queuemsgf(&bpi, BPMSG_SKIP, "%s %ld %ld",
507 pi->recv->net_name, (long)pi->recv->bytes_total,
508 (long)pi->recv->mod_time);
509 }
510 else if( pi->recv->bytes_received == pi->recv->bytes_total )
511 {
512 recv_file = FALSE;
513 pi->recv->status = FSTAT_SUCCESS;
514 if( !p_rx_fclose(pi) )
515 {
516 binkp_queuemsgf(&bpi, BPMSG_GOT, "%s %ld %ld",
517 pi->recv->net_name, (long)pi->recv->bytes_total,
518 (long)pi->recv->mod_time);
519 }
520 else
521 {
522 binkp_queuemsgf(&bpi, BPMSG_SKIP, "%s %ld %ld",
523 pi->recv->net_name, (long)pi->recv->bytes_total,
524 (long)pi->recv->mod_time);
525 }
526 }
527 }
528 }
529 #ifdef DEBUG
530 else
531 DEB((D_PROT, "ignore received data block"));
532 #endif
533 break;
534
535 case BPMSG_FILE:
536 if( recv_file )
537 { p_rx_fclose(pi); recv_file = FALSE; }
538
539 if( binkp_parsfinfo(bpi.ibuf+1, &fname,
540 &fsize, &ftime, &foffs) )
541 {
542 binkp_queuemsg(&bpi, BPMSG_ERR, "FILE: ", "unparsable arguments");
543 goto FinishSession;
544 }
545
546 if( pi->recv && !p_compfinfo(pi->recv, fname, fsize, ftime)
547 && pi->recv->bytes_skipped == foffs )
548 {
549 recv_file = TRUE;
550 break;
551 }
552
553 switch(p_rx_fopen(pi, fname, fsize, ftime, 0)) {
554 case 0:
555 if( pi->recv->bytes_skipped == 0 )
556 {
557 recv_file = TRUE;
558 break;
559 }
560 binkp_queuemsgf(&bpi, BPMSG_GET, "%s %ld %ld %ld",
561 pi->recv->net_name, (long)pi->recv->bytes_total,
562 (long)pi->recv->mod_time,
563 (long)pi->recv->bytes_skipped);
564 break;
565 case 1:
566 /* SKIP (non-destructive) */
567 binkp_queuemsgf(&bpi, BPMSG_SKIP, "%s %ld %ld",
568 pi->recv->net_name, (long)pi->recv->bytes_total,
569 (long)pi->recv->mod_time);
570 break;
571 case 2:
572 /* SKIP (destructive) */
573 binkp_queuemsgf(&bpi, BPMSG_GOT, "%s %ld %ld",
574 pi->recv->net_name, (long)pi->recv->bytes_total,
575 (long)pi->recv->mod_time);
576 break;
577 default:
578 ASSERT_MSG();
579 }
580 break;
581
582 case BPMSG_EOB: /* End Of Batch */
583 if( recv_file )
584 {
585 p_rx_fclose(pi);
586 recv_file = FALSE;
587 }
588 rcvd_EOB = TRUE;
589 break;
590
591 case BPMSG_GOT:
592 case BPMSG_SKIP:
593 if( binkp_parsfinfo(bpi.ibuf+1, &fname, &fsize, &ftime, NULL) == 0 )
594 {
595 if( send_file && !p_compfinfo(pi->send, fname, fsize, ftime) )
596 {
597 /* We got GOT/SKIP for current file! */
598 if( recv_rc == BPMSG_GOT )
599 pi->send->status = FSTAT_SKIPPED;
600 else
601 pi->send->status = FSTAT_REFUSED;
602 p_tx_fclose(pi);
603 send_file = FALSE;
604 break;
605 }
606
607 wait_got = FALSE;
608
609 for( i = 0; i < pi->n_sentfiles; i++ )
610 {
611 if( pi->sentfiles[i].status == FSTAT_WAITACK
612 && !p_compfinfo(&pi->sentfiles[i], fname, fsize, ftime) )
613 {
614 s_finfo *tmp = pi->send;
615
616 pi->send = &pi->sentfiles[i];
617 if( recv_rc == BPMSG_GOT )
618 pi->send->status = FSTAT_SUCCESS;
619 else
620 pi->send->status = FSTAT_REFUSED;
621 p_tx_fclose(pi);
622
623 pi->send = tmp;
624 break;
625 }
626 else if( pi->sentfiles[i].status == FSTAT_WAITACK )
627 wait_got = TRUE;
628 }
629
630 /*
631 * Check, maybe there are files still
632 * waiting for the GOT/SKIP acknwoledge
633 */
634 while( i < pi->n_sentfiles && !wait_got )
635 {
636 if( pi->sentfiles[i].status == FSTAT_WAITACK )
637 wait_got = TRUE;
638 ++i;
639 }
640 }
641 break;
642
643 case BPMSG_ERR:
644 bf_log("remote report error: \"%s\"", bpi.ibuf+1);
645 gotoexit(PRC_ERROR);
646 break;
647
648 case BPMSG_BSY:
649 bf_log("remote busy error: \"%s\"", bpi.ibuf+1);
650 gotoexit(PRC_ERROR);
651 break;
652
653 case BPMSG_GET:
654 if( binkp_parsfinfo(bpi.ibuf+1, &fname, &fsize, &ftime, &foffs) == 0 )
655 {
656 if( send_file && !p_compfinfo(pi->send, fname, fsize, ftime) )
657 {
658 if( fseek(pi->send->fp, foffs, SEEK_SET) == -1 )
659 {
660 bf_log("cannot send file from requested offset %ld", (long)foffs);
661 p_tx_fclose(pi);
662 send_file = FALSE;
663 }
664 else
665 {
666 bf_log("sending \"%s\" from %ld offset",
667 pi->send->fname, (long)foffs);
668 pi->send->bytes_skipped = foffs;
669 pi->send->bytes_sent = foffs;
670 binkp_queuemsgf(&bpi, BPMSG_FILE, "%s %ld %ld %ld",
671 pi->send->net_name, (long)pi->send->bytes_total,
672 (long)pi->send->mod_time, (long)foffs);
673 }
674 }
675 }
676 break;
677
678 default:
679 bf_log("binkp got unhandled msg #%d", recv_rc);
680 break;
681 }
682 } /* end of while( !sent_EOB || !rcvd_EOB ) */
683
684 FinishSession:
685 if( binkp_flush_queue(&bpi, bpi.timeout) && rc == PRC_NOERROR )
686 rc = PRC_ERROR;
687
688 exit:
689 if( pi->send && pi->send->fp ) p_tx_fclose(pi);
690 if( pi->recv && pi->recv->fp ) p_rx_fclose(pi);
691
692 binkp_deinit_bpinfo(&bpi);
693
694 DEB((D_PROT, "binkp: BINKP exit = %d", rc));
695
696 return rc;
697 }
698
699