1 /* $OpenBSD: ftp-proxy.c,v 1.41 2005/03/05 23:11:19 cloder Exp $ */ 2 3 /* 4 * Copyright (c) 1996-2001 5 * Obtuse Systems Corporation. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the Obtuse Systems nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY OBTUSE SYSTEMS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL OBTUSE SYSTEMS CORPORATION OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 * 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 /* 37 * ftp proxy, Originally based on juniper_ftp_proxy from the Obtuse 38 * Systems juniper firewall, written by Dan Boulet <danny@obtuse.com> 39 * and Bob Beck <beck@obtuse.com> 40 * 41 * This version basically passes everything through unchanged except 42 * for the PORT and the * "227 Entering Passive Mode" reply. 43 * 44 * A PORT command is handled by noting the IP address and port number 45 * specified and then configuring a listen port on some very high port 46 * number and telling the server about it using a PORT message. 47 * We then watch for an in-bound connection on the port from the server 48 * and connect to the client's port when it happens. 49 * 50 * A "227 Entering Passive Mode" reply is handled by noting the IP address 51 * and port number specified and then configuring a listen port on some 52 * very high port number and telling the client about it using a 53 * "227 Entering Passive Mode" reply. 54 * We then watch for an in-bound connection on the port from the client 55 * and connect to the server's port when it happens. 56 * 57 * supports tcp wrapper lookups/access control with the -w flag using 58 * the real destination address - the tcp wrapper stuff is done after 59 * the real destination address is retrieved from pf 60 * 61 */ 62 63 /* 64 * TODO: 65 * Plenty, this is very basic, with the idea to get it in clean first. 66 * 67 * - IPv6 and EPASV support 68 * - Content filter support 69 * - filename filter support 70 * - per-user rules perhaps. 71 */ 72 73 #include <sys/param.h> 74 #include <sys/time.h> 75 #include <sys/socket.h> 76 77 #include <net/if.h> 78 #include <netinet/in.h> 79 80 #include <arpa/inet.h> 81 82 #include <ctype.h> 83 #include <errno.h> 84 #include <grp.h> 85 #include <netdb.h> 86 #include <pwd.h> 87 #include <signal.h> 88 #include <stdarg.h> 89 #include <stdio.h> 90 #include <stdlib.h> 91 #include <string.h> 92 #include <sysexits.h> 93 #include <syslog.h> 94 #include <unistd.h> 95 96 #include "util.h" 97 98 #ifdef LIBWRAP 99 #include <tcpd.h> 100 int allow_severity = LOG_INFO; 101 int deny_severity = LOG_NOTICE; 102 #endif /* LIBWRAP */ 103 104 int min_port = IPPORT_HIFIRSTAUTO; 105 int max_port = IPPORT_HILASTAUTO; 106 107 #define STARTBUFSIZE 1024 /* Must be at least 3 */ 108 109 /* 110 * Variables used to support PORT mode connections. 111 * 112 * This gets a bit complicated. 113 * 114 * If PORT mode is on then client_listen_sa describes the socket that 115 * the real client is listening on and server_listen_sa describes the 116 * socket that we are listening on (waiting for the real server to connect 117 * with us). 118 * 119 * If PASV mode is on then client_listen_sa describes the socket that 120 * we are listening on (waiting for the real client to connect to us on) 121 * and server_listen_sa describes the socket that the real server is 122 * listening on. 123 * 124 * If the socket we are listening on gets a connection then we connect 125 * to the other side's socket. Similarly, if a connected socket is 126 * shutdown then we shutdown the other side's socket. 127 */ 128 129 double xfer_start_time; 130 131 struct sockaddr_in real_server_sa; 132 struct sockaddr_in client_listen_sa; 133 struct sockaddr_in server_listen_sa; 134 struct sockaddr_in proxy_sa; 135 struct in_addr src_addr; 136 137 int client_listen_socket = -1; /* Only used in PASV mode */ 138 int client_data_socket = -1; /* Connected socket to real client */ 139 int server_listen_socket = -1; /* Only used in PORT mode */ 140 int server_data_socket = -1; /* Connected socket to real server */ 141 int client_data_bytes, server_data_bytes; 142 143 int AnonFtpOnly; 144 int Verbose; 145 int NatMode; 146 int ReverseMode; 147 148 char ClientName[NI_MAXHOST]; 149 char RealServerName[NI_MAXHOST]; 150 char OurName[NI_MAXHOST]; 151 152 const char *User = "proxy"; 153 const char *Group; 154 155 extern int Debug_Level; 156 extern int Use_Rdns; 157 extern in_addr_t Bind_Addr; 158 extern char *__progname; 159 160 typedef enum { 161 UNKNOWN_MODE, 162 PORT_MODE, 163 PASV_MODE, 164 EPRT_MODE, 165 EPSV_MODE 166 } connection_mode_t; 167 168 connection_mode_t connection_mode; 169 170 extern void debuglog(int debug_level, const char *fmt, ...); 171 double wallclock_time(void); 172 void show_xfer_stats(void); 173 void log_control_command (char *cmd, int client); 174 int new_dataconn(int server); 175 void do_client_cmd(struct csiob *client, struct csiob *server); 176 void do_server_reply(struct csiob *server, struct csiob *client); 177 static void 178 usage(void) 179 { 180 syslog(LOG_NOTICE, 181 "usage: %s [-AnrVw] [-a address] [-D debuglevel] [-g group]" 182 " [-M maxport] [-m minport] [-R address[:port]] [-S address]" 183 " [-t timeout] [-u user]", __progname); 184 exit(EX_USAGE); 185 } 186 187 static void 188 close_client_data(void) 189 { 190 if (client_data_socket >= 0) { 191 shutdown(client_data_socket, 2); 192 close(client_data_socket); 193 client_data_socket = -1; 194 } 195 } 196 197 static void 198 close_server_data(void) 199 { 200 if (server_data_socket >= 0) { 201 shutdown(server_data_socket, 2); 202 close(server_data_socket); 203 server_data_socket = -1; 204 } 205 } 206 207 static void 208 drop_privs(void) 209 { 210 struct passwd *pw; 211 struct group *gr; 212 uid_t uid = 0; 213 gid_t gid = 0; 214 215 if (User != NULL) { 216 pw = getpwnam(User); 217 if (pw == NULL) { 218 syslog(LOG_ERR, "cannot find user %s", User); 219 exit(EX_USAGE); 220 } 221 uid = pw->pw_uid; 222 gid = pw->pw_gid; 223 } 224 225 if (Group != NULL) { 226 gr = getgrnam(Group); 227 if (gr == NULL) { 228 syslog(LOG_ERR, "cannot find group %s", Group); 229 exit(EX_USAGE); 230 } 231 gid = gr->gr_gid; 232 } 233 234 if (gid != 0 && (setegid(gid) == -1 || setgid(gid) == -1)) { 235 syslog(LOG_ERR, "cannot drop group privs (%m)"); 236 exit(EX_CONFIG); 237 } 238 239 if (uid != 0 && (seteuid(uid) == -1 || setuid(uid) == -1)) { 240 syslog(LOG_ERR, "cannot drop root privs (%m)"); 241 exit(EX_CONFIG); 242 } 243 } 244 245 #ifdef LIBWRAP 246 /* 247 * Check a connection against the tcpwrapper, log if we're going to 248 * reject it, returns: 0 -> reject, 1 -> accept. We add in hostnames 249 * if we are set to do reverse DNS, otherwise no. 250 */ 251 static int 252 check_host(struct sockaddr_in *client_sin, struct sockaddr_in *server_sin) 253 { 254 char cname[NI_MAXHOST]; 255 char sname[NI_MAXHOST]; 256 struct request_info request; 257 int i; 258 259 request_init(&request, RQ_DAEMON, __progname, RQ_CLIENT_SIN, 260 client_sin, RQ_SERVER_SIN, server_sin, RQ_CLIENT_ADDR, 261 inet_ntoa(client_sin->sin_addr), 0); 262 263 if (Use_Rdns) { 264 /* 265 * We already looked these up, but we have to do it again 266 * for tcp wrapper, to ensure that we get the DNS name, since 267 * the tcp wrapper cares about these things, and we don't 268 * want to pass in a printed address as a name. 269 */ 270 i = getnameinfo((struct sockaddr *) &client_sin->sin_addr, 271 sizeof(&client_sin->sin_addr), cname, sizeof(cname), 272 NULL, 0, NI_NAMEREQD); 273 274 if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) 275 strlcpy(cname, STRING_UNKNOWN, sizeof(cname)); 276 277 i = getnameinfo((struct sockaddr *)&server_sin->sin_addr, 278 sizeof(&server_sin->sin_addr), sname, sizeof(sname), 279 NULL, 0, NI_NAMEREQD); 280 281 if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) 282 strlcpy(sname, STRING_UNKNOWN, sizeof(sname)); 283 } else { 284 /* 285 * ensure the TCP wrapper doesn't start doing 286 * reverse DNS lookups if we aren't supposed to. 287 */ 288 strlcpy(cname, STRING_UNKNOWN, sizeof(cname)); 289 strlcpy(sname, STRING_UNKNOWN, sizeof(sname)); 290 } 291 292 request_set(&request, RQ_SERVER_ADDR, inet_ntoa(server_sin->sin_addr), 293 0); 294 request_set(&request, RQ_CLIENT_NAME, cname, RQ_SERVER_NAME, sname, 0); 295 296 if (!hosts_access(&request)) { 297 syslog(LOG_NOTICE, "tcpwrappers rejected: %s -> %s", 298 ClientName, RealServerName); 299 return(0); 300 } 301 return(1); 302 } 303 #endif /* LIBWRAP */ 304 305 double 306 wallclock_time(void) 307 { 308 struct timeval tv; 309 310 gettimeofday(&tv, NULL); 311 return(tv.tv_sec + tv.tv_usec / 1e6); 312 } 313 314 /* 315 * Show the stats for this data transfer 316 */ 317 void 318 show_xfer_stats(void) 319 { 320 char tbuf[1000]; 321 double delta; 322 size_t len; 323 int i = -1; 324 325 if (!Verbose) 326 return; 327 328 delta = wallclock_time() - xfer_start_time; 329 330 if (delta < 0.001) 331 delta = 0.001; 332 333 if (client_data_bytes == 0 && server_data_bytes == 0) { 334 syslog(LOG_INFO, 335 "data transfer complete (no bytes transferred)"); 336 return; 337 } 338 339 len = sizeof(tbuf); 340 341 if (delta >= 60) { 342 int idelta; 343 344 idelta = delta + 0.5; 345 if (idelta >= 60*60) { 346 i = snprintf(tbuf, len, 347 "data transfer complete (%dh %dm %ds", 348 idelta / (60*60), (idelta % (60*60)) / 60, 349 idelta % 60); 350 if (i == -1 || i >= len) 351 goto logit; 352 len -= i; 353 } else { 354 i = snprintf(tbuf, len, 355 "data transfer complete (%dm %ds", idelta / 60, 356 idelta % 60); 357 if (i == -1 || i >= len) 358 goto logit; 359 len -= i; 360 } 361 } else { 362 i = snprintf(tbuf, len, "data transfer complete (%.1fs", 363 delta); 364 if (i == -1 || i >= len) 365 goto logit; 366 len -= i; 367 } 368 369 if (client_data_bytes > 0) { 370 i = snprintf(&tbuf[strlen(tbuf)], len, 371 ", %d bytes to server) (%.1fKB/s", client_data_bytes, 372 (client_data_bytes / delta) / (double)1024); 373 if (i == -1 || i >= len) 374 goto logit; 375 len -= i; 376 } 377 if (server_data_bytes > 0) { 378 i = snprintf(&tbuf[strlen(tbuf)], len, 379 ", %d bytes to client) (%.1fKB/s", server_data_bytes, 380 (server_data_bytes / delta) / (double)1024); 381 if (i == -1 || i >= len) 382 goto logit; 383 len -= i; 384 } 385 strlcat(tbuf, ")", sizeof(tbuf)); 386 logit: 387 if (i != -1) 388 syslog(LOG_INFO, "%s", tbuf); 389 } 390 391 void 392 log_control_command (char *cmd, int client) 393 { 394 /* log an ftp control command or reply */ 395 const char *logstring; 396 int level = LOG_DEBUG; 397 398 if (!Verbose) 399 return; 400 401 /* don't log passwords */ 402 if (strncasecmp(cmd, "pass ", 5) == 0) 403 logstring = "PASS XXXX"; 404 else 405 logstring = cmd; 406 if (client) { 407 /* log interesting stuff at LOG_INFO, rest at LOG_DEBUG */ 408 if ((strncasecmp(cmd, "user ", 5) == 0) || 409 (strncasecmp(cmd, "retr ", 5) == 0) || 410 (strncasecmp(cmd, "cwd ", 4) == 0) || 411 (strncasecmp(cmd, "stor " ,5) == 0)) 412 level = LOG_INFO; 413 } 414 syslog(level, "%s %s", client ? "client:" : " server:", 415 logstring); 416 } 417 418 /* 419 * set ourselves up for a new data connection. Direction is toward client if 420 * "server" is 0, towards server otherwise. 421 */ 422 int 423 new_dataconn(int server) 424 { 425 /* 426 * Close existing data conn. 427 */ 428 429 if (client_listen_socket != -1) { 430 close(client_listen_socket); 431 client_listen_socket = -1; 432 } 433 close_client_data(); 434 435 if (server_listen_socket != -1) { 436 close(server_listen_socket); 437 server_listen_socket = -1; 438 } 439 close_server_data(); 440 441 if (server) { 442 bzero(&server_listen_sa, sizeof(server_listen_sa)); 443 server_listen_socket = get_backchannel_socket(SOCK_STREAM, 444 min_port, max_port, -1, 1, &server_listen_sa); 445 446 if (server_listen_socket == -1) { 447 syslog(LOG_INFO, "server socket bind() failed (%m)"); 448 exit(EX_OSERR); 449 } 450 if (listen(server_listen_socket, 5) != 0) { 451 syslog(LOG_INFO, "server socket listen() failed (%m)"); 452 exit(EX_OSERR); 453 } 454 } else { 455 bzero(&client_listen_sa, sizeof(client_listen_sa)); 456 client_listen_socket = get_backchannel_socket(SOCK_STREAM, 457 min_port, max_port, -1, 1, &client_listen_sa); 458 459 if (client_listen_socket == -1) { 460 syslog(LOG_NOTICE, 461 "cannot get client listen socket (%m)"); 462 exit(EX_OSERR); 463 } 464 if (listen(client_listen_socket, 5) != 0) { 465 syslog(LOG_NOTICE, 466 "cannot listen on client socket (%m)"); 467 exit(EX_OSERR); 468 } 469 } 470 return(0); 471 } 472 473 static void 474 connect_pasv_backchannel(void) 475 { 476 struct sockaddr_in listen_sa; 477 socklen_t salen; 478 479 /* 480 * We are about to accept a connection from the client. 481 * This is a PASV data connection. 482 */ 483 debuglog(2, "client listen socket ready"); 484 485 close_server_data(); 486 close_client_data(); 487 488 salen = sizeof(listen_sa); 489 client_data_socket = accept(client_listen_socket, 490 (struct sockaddr *)&listen_sa, &salen); 491 492 if (client_data_socket < 0) { 493 syslog(LOG_NOTICE, "accept() failed (%m)"); 494 exit(EX_OSERR); 495 } 496 close(client_listen_socket); 497 client_listen_socket = -1; 498 memset(&listen_sa, 0, sizeof(listen_sa)); 499 500 server_data_socket = get_backchannel_socket(SOCK_STREAM, min_port, 501 max_port, -1, 1, &listen_sa); 502 if (server_data_socket < 0) { 503 syslog(LOG_NOTICE, "get_backchannel_socket() failed (%m)"); 504 exit(EX_OSERR); 505 } 506 if (connect(server_data_socket, (struct sockaddr *) &server_listen_sa, 507 sizeof(server_listen_sa)) != 0) { 508 syslog(LOG_NOTICE, "connect() failed (%m)"); 509 exit(EX_NOHOST); 510 } 511 client_data_bytes = 0; 512 server_data_bytes = 0; 513 xfer_start_time = wallclock_time(); 514 } 515 516 static void 517 connect_port_backchannel(void) 518 { 519 struct sockaddr_in listen_sa; 520 socklen_t salen; 521 522 /* 523 * We are about to accept a connection from the server. 524 * This is a PORT or EPRT data connection. 525 */ 526 debuglog(2, "server listen socket ready"); 527 528 close_server_data(); 529 close_client_data(); 530 531 salen = sizeof(listen_sa); 532 server_data_socket = accept(server_listen_socket, 533 (struct sockaddr *)&listen_sa, &salen); 534 if (server_data_socket < 0) { 535 syslog(LOG_NOTICE, "accept() failed (%m)"); 536 exit(EX_OSERR); 537 } 538 close(server_listen_socket); 539 server_listen_socket = -1; 540 541 if (getuid() != 0) { 542 /* 543 * We're not running as root, so we get a backchannel 544 * socket bound in our designated range, instead of 545 * getting one bound to port 20 - This is deliberately 546 * not RFC compliant. 547 */ 548 bcopy(&src_addr, &listen_sa.sin_addr, sizeof(struct in_addr)); 549 client_data_socket = get_backchannel_socket(SOCK_STREAM, 550 min_port, max_port, -1, 1, &listen_sa); 551 if (client_data_socket < 0) { 552 syslog(LOG_NOTICE, "get_backchannel_socket() failed (%m)"); 553 exit(EX_OSERR); 554 } 555 556 } else { 557 558 /* 559 * We're root, get our backchannel socket bound to port 560 * 20 here, so we're fully RFC compliant. 561 */ 562 client_data_socket = socket(AF_INET, SOCK_STREAM, 0); 563 564 salen = 1; 565 listen_sa.sin_family = AF_INET; 566 bcopy(&src_addr, &listen_sa.sin_addr, sizeof(struct in_addr)); 567 listen_sa.sin_port = htons(20); 568 569 if (setsockopt(client_data_socket, SOL_SOCKET, SO_REUSEADDR, 570 &salen, sizeof(salen)) == -1) { 571 syslog(LOG_NOTICE, "setsockopt() failed (%m)"); 572 exit(EX_OSERR); 573 } 574 575 if (bind(client_data_socket, (struct sockaddr *)&listen_sa, 576 sizeof(listen_sa)) == - 1) { 577 syslog(LOG_NOTICE, "data channel bind() failed (%m)"); 578 exit(EX_OSERR); 579 } 580 } 581 582 if (connect(client_data_socket, (struct sockaddr *) &client_listen_sa, 583 sizeof(client_listen_sa)) != 0) { 584 syslog(LOG_INFO, "cannot connect data channel (%m)"); 585 exit(EX_NOHOST); 586 } 587 588 client_data_bytes = 0; 589 server_data_bytes = 0; 590 xfer_start_time = wallclock_time(); 591 } 592 593 void 594 do_client_cmd(struct csiob *client, struct csiob *server) 595 { 596 int i, j, rv; 597 char tbuf[100]; 598 char *sendbuf = NULL; 599 600 log_control_command((char *)client->line_buffer, 1); 601 602 /* client->line_buffer is an ftp control command. 603 * There is no reason for these to be very long. 604 * In the interest of limiting buffer overrun attempts, 605 * we catch them here. 606 */ 607 if (strlen((char *)client->line_buffer) > 512) { 608 syslog(LOG_NOTICE, "excessively long control command"); 609 exit(EX_DATAERR); 610 } 611 612 /* 613 * Check the client user provided if needed 614 */ 615 if (AnonFtpOnly && strncasecmp((char *)client->line_buffer, "user ", 616 strlen("user ")) == 0) { 617 char *cp; 618 619 cp = (char *) client->line_buffer + strlen("user "); 620 if ((strcasecmp(cp, "ftp\r\n") != 0) && 621 (strcasecmp(cp, "anonymous\r\n") != 0)) { 622 /* 623 * this isn't anonymous - give the client an 624 * error before they send a password 625 */ 626 snprintf(tbuf, sizeof(tbuf), 627 "500 Only anonymous FTP is allowed\r\n"); 628 j = 0; 629 i = strlen(tbuf); 630 do { 631 rv = send(client->fd, tbuf + j, i - j, 0); 632 if (rv == -1 && errno != EAGAIN && 633 errno != EINTR) 634 break; 635 else if (rv != -1) 636 j += rv; 637 } while (j >= 0 && j < i); 638 sendbuf = NULL; 639 } else 640 sendbuf = (char *)client->line_buffer; 641 } else if ((strncasecmp((char *)client->line_buffer, "eprt ", 642 strlen("eprt ")) == 0)) { 643 644 /* Watch out for EPRT commands */ 645 char *line = NULL, *q, *p, *result[3], delim; 646 struct addrinfo hints, *res = NULL; 647 unsigned long proto; 648 649 j = 0; 650 line = strdup((char *)client->line_buffer+strlen("eprt ")); 651 if (line == NULL) { 652 syslog(LOG_ERR, "insufficient memory"); 653 exit(EX_UNAVAILABLE); 654 } 655 p = line; 656 delim = p[0]; 657 p++; 658 659 memset(result,0, sizeof(result)); 660 for (i = 0; i < 3; i++) { 661 q = strchr(p, delim); 662 if (!q || *q != delim) 663 goto parsefail; 664 *q++ = '\0'; 665 result[i] = p; 666 p = q; 667 } 668 669 proto = strtoul(result[0], &p, 10); 670 if (!*result[0] || *p) 671 goto protounsupp; 672 673 memset(&hints, 0, sizeof(hints)); 674 if (proto != 1) /* 1 == AF_INET - all we support for now */ 675 goto protounsupp; 676 hints.ai_family = AF_INET; 677 hints.ai_socktype = SOCK_STREAM; 678 hints.ai_flags = AI_NUMERICHOST; /*no DNS*/ 679 if (getaddrinfo(result[1], result[2], &hints, &res)) 680 goto parsefail; 681 if (res->ai_next) 682 goto parsefail; 683 if (sizeof(client_listen_sa) < res->ai_addrlen) 684 goto parsefail; 685 memcpy(&client_listen_sa, res->ai_addr, res->ai_addrlen); 686 687 debuglog(1, "client wants us to use %s:%u", 688 inet_ntoa(client_listen_sa.sin_addr), 689 htons(client_listen_sa.sin_port)); 690 691 /* 692 * Configure our own listen socket and tell the server about it 693 */ 694 new_dataconn(1); 695 connection_mode = EPRT_MODE; 696 697 debuglog(1, "we want server to use %s:%u", 698 inet_ntoa(server->sa.sin_addr), 699 ntohs(server_listen_sa.sin_port)); 700 701 snprintf(tbuf, sizeof(tbuf), "EPRT |%d|%s|%u|\r\n", 1, 702 inet_ntoa(server->sa.sin_addr), 703 ntohs(server_listen_sa.sin_port)); 704 debuglog(1, "to server (modified): %s", tbuf); 705 sendbuf = tbuf; 706 goto out; 707 parsefail: 708 snprintf(tbuf, sizeof(tbuf), 709 "500 Invalid argument; rejected\r\n"); 710 sendbuf = NULL; 711 goto out; 712 protounsupp: 713 /* we only support AF_INET for now */ 714 if (proto == 2) 715 snprintf(tbuf, sizeof(tbuf), 716 "522 Protocol not supported, use (1)\r\n"); 717 else 718 snprintf(tbuf, sizeof(tbuf), 719 "501 Protocol not supported\r\n"); 720 sendbuf = NULL; 721 out: 722 if (line) 723 free(line); 724 if (res) 725 freeaddrinfo(res); 726 if (sendbuf == NULL) { 727 debuglog(1, "to client (modified): %s", tbuf); 728 i = strlen(tbuf); 729 do { 730 rv = send(client->fd, tbuf + j, i - j, 0); 731 if (rv == -1 && errno != EAGAIN && 732 errno != EINTR) 733 break; 734 else if (rv != -1) 735 j += rv; 736 } while (j >= 0 && j < i); 737 } 738 } else if (!NatMode && (strncasecmp((char *)client->line_buffer, 739 "epsv", strlen("epsv")) == 0)) { 740 741 /* 742 * If we aren't in NAT mode, deal with EPSV. 743 * EPSV is a problem - Unlike PASV, the reply from the 744 * server contains *only* a port, we can't modify the reply 745 * to the client and get the client to connect to us without 746 * resorting to using a dynamic rdr rule we have to add in 747 * for the reply to this connection, and take away afterwards. 748 * so this will wait until we have the right solution for rule 749 * additions/deletions in pf. 750 * 751 * in the meantime we just tell the client we don't do it, 752 * and most clients should fall back to using PASV. 753 */ 754 755 snprintf(tbuf, sizeof(tbuf), 756 "500 EPSV command not understood\r\n"); 757 debuglog(1, "to client (modified): %s", tbuf); 758 j = 0; 759 i = strlen(tbuf); 760 do { 761 rv = send(client->fd, tbuf + j, i - j, 0); 762 if (rv == -1 && errno != EAGAIN && errno != EINTR) 763 break; 764 else if (rv != -1) 765 j += rv; 766 } while (j >= 0 && j < i); 767 sendbuf = NULL; 768 } else if (strncasecmp((char *)client->line_buffer, "port ", 769 strlen("port ")) == 0) { 770 unsigned int values[6]; 771 char *tailptr; 772 773 debuglog(1, "Got a PORT command"); 774 775 tailptr = (char *)&client->line_buffer[strlen("port ")]; 776 values[0] = 0; 777 778 i = sscanf(tailptr, "%u,%u,%u,%u,%u,%u", &values[0], 779 &values[1], &values[2], &values[3], &values[4], 780 &values[5]); 781 if (i != 6) { 782 syslog(LOG_INFO, "malformed PORT command (%s)", 783 client->line_buffer); 784 exit(EX_DATAERR); 785 } 786 787 for (i = 0; i<6; i++) { 788 if (values[i] > 255) { 789 syslog(LOG_INFO, 790 "malformed PORT command (%s)", 791 client->line_buffer); 792 exit(EX_DATAERR); 793 } 794 } 795 796 client_listen_sa.sin_family = AF_INET; 797 client_listen_sa.sin_addr.s_addr = htonl((values[0] << 24) | 798 (values[1] << 16) | (values[2] << 8) | 799 (values[3] << 0)); 800 801 client_listen_sa.sin_port = htons((values[4] << 8) | 802 values[5]); 803 debuglog(1, "client wants us to use %u.%u.%u.%u:%u", 804 values[0], values[1], values[2], values[3], 805 (values[4] << 8) | values[5]); 806 807 /* 808 * Configure our own listen socket and tell the server about it 809 */ 810 new_dataconn(1); 811 connection_mode = PORT_MODE; 812 813 debuglog(1, "we want server to use %s:%u", 814 inet_ntoa(server->sa.sin_addr), 815 ntohs(server_listen_sa.sin_port)); 816 817 snprintf(tbuf, sizeof(tbuf), "PORT %u,%u,%u,%u,%u,%u\r\n", 818 ((u_char *)&server->sa.sin_addr.s_addr)[0], 819 ((u_char *)&server->sa.sin_addr.s_addr)[1], 820 ((u_char *)&server->sa.sin_addr.s_addr)[2], 821 ((u_char *)&server->sa.sin_addr.s_addr)[3], 822 ((u_char *)&server_listen_sa.sin_port)[0], 823 ((u_char *)&server_listen_sa.sin_port)[1]); 824 825 debuglog(1, "to server (modified): %s", tbuf); 826 827 sendbuf = tbuf; 828 } else 829 sendbuf = (char *)client->line_buffer; 830 831 /* 832 *send our (possibly modified) control command in sendbuf 833 * on it's way to the server 834 */ 835 if (sendbuf != NULL) { 836 j = 0; 837 i = strlen(sendbuf); 838 do { 839 rv = send(server->fd, sendbuf + j, i - j, 0); 840 if (rv == -1 && errno != EAGAIN && errno != EINTR) 841 break; 842 else if (rv != -1) 843 j += rv; 844 } while (j >= 0 && j < i); 845 } 846 } 847 848 void 849 do_server_reply(struct csiob *server, struct csiob *client) 850 { 851 int code, i, j, rv; 852 struct in_addr *iap; 853 static int continuing = 0; 854 char tbuf[100], *sendbuf, *p; 855 856 log_control_command((char *)server->line_buffer, 0); 857 858 if (strlen((char *)server->line_buffer) > 512) { 859 /* 860 * someone's playing games. Have a cow in the syslogs and 861 * exit - we don't pass this on for fear of hurting 862 * our other end, which might be poorly implemented. 863 */ 864 syslog(LOG_NOTICE, "long FTP control reply"); 865 exit(EX_DATAERR); 866 } 867 868 /* 869 * Watch out for "227 Entering Passive Mode ..." replies 870 */ 871 code = strtol((char *)server->line_buffer, &p, 10); 872 if (isspace(server->line_buffer[0])) 873 code = 0; 874 if (!*(server->line_buffer) || (*p != ' ' && *p != '-')) { 875 if (continuing) 876 goto sendit; 877 syslog(LOG_INFO, "malformed control reply"); 878 exit(EX_DATAERR); 879 } 880 if (code <= 0 || code > 999) { 881 if (continuing) 882 goto sendit; 883 syslog(LOG_INFO, "invalid server reply code %d", code); 884 exit(EX_DATAERR); 885 } 886 if (*p == '-') 887 continuing = 1; 888 else 889 continuing = 0; 890 if (code == 227 && !NatMode) { 891 unsigned int values[6]; 892 char *tailptr; 893 894 debuglog(1, "Got a PASV reply"); 895 debuglog(1, "{%s}", (char *)server->line_buffer); 896 897 tailptr = (char *)strchr((char *)server->line_buffer, '('); 898 if (tailptr == NULL) { 899 tailptr = strrchr((char *)server->line_buffer, ' '); 900 if (tailptr == NULL) { 901 syslog(LOG_NOTICE, "malformed 227 reply"); 902 exit(EX_DATAERR); 903 } 904 } 905 tailptr++; /* skip past space or ( */ 906 907 values[0] = 0; 908 909 i = sscanf(tailptr, "%u,%u,%u,%u,%u,%u", &values[0], 910 &values[1], &values[2], &values[3], &values[4], 911 &values[5]); 912 if (i != 6) { 913 syslog(LOG_INFO, "malformed PASV reply (%s)", 914 client->line_buffer); 915 exit(EX_DATAERR); 916 } 917 for (i = 0; i<6; i++) 918 if (values[i] > 255) { 919 syslog(LOG_INFO, "malformed PASV reply(%s)", 920 client->line_buffer); 921 exit(EX_DATAERR); 922 } 923 924 server_listen_sa.sin_family = AF_INET; 925 server_listen_sa.sin_addr.s_addr = htonl((values[0] << 24) | 926 (values[1] << 16) | (values[2] << 8) | (values[3] << 0)); 927 server_listen_sa.sin_port = htons((values[4] << 8) | 928 values[5]); 929 930 debuglog(1, "server wants us to use %s:%u", 931 inet_ntoa(server_listen_sa.sin_addr), (values[4] << 8) | 932 values[5]); 933 934 new_dataconn(0); 935 connection_mode = PASV_MODE; 936 if (ReverseMode) 937 iap = &(proxy_sa.sin_addr); 938 else 939 iap = &(server->sa.sin_addr); 940 941 debuglog(1, "we want client to use %s:%u", inet_ntoa(*iap), 942 htons(client_listen_sa.sin_port)); 943 944 snprintf(tbuf, sizeof(tbuf), 945 "227 Entering Passive Mode (%u,%u,%u,%u,%u,%u)\r\n", 946 ((u_char *)iap)[0], ((u_char *)iap)[1], 947 ((u_char *)iap)[2], ((u_char *)iap)[3], 948 ((u_char *)&client_listen_sa.sin_port)[0], 949 ((u_char *)&client_listen_sa.sin_port)[1]); 950 debuglog(1, "to client (modified): %s", tbuf); 951 sendbuf = tbuf; 952 } else { 953 sendit: 954 sendbuf = (char *)server->line_buffer; 955 } 956 957 /* 958 * send our (possibly modified) control command in sendbuf 959 * on it's way to the client 960 */ 961 j = 0; 962 i = strlen(sendbuf); 963 do { 964 rv = send(client->fd, sendbuf + j, i - j, 0); 965 if (rv == -1 && errno != EAGAIN && errno != EINTR) 966 break; 967 else if (rv != -1) 968 j += rv; 969 } while (j >= 0 && j < i); 970 971 } 972 973 int 974 main(int argc, char *argv[]) 975 { 976 struct csiob client_iob, server_iob; 977 struct sigaction new_sa, old_sa; 978 int sval, ch, flags, i; 979 socklen_t salen; 980 int one = 1; 981 long timeout_seconds = 0; 982 struct timeval tv; 983 #ifdef LIBWRAP 984 int use_tcpwrapper = 0; 985 #endif /* LIBWRAP */ 986 987 while ((ch = getopt(argc, argv, "a:D:g:m:M:R:S:t:u:AnVwr")) != -1) { 988 char *p; 989 switch (ch) { 990 case 'a': 991 if (!*optarg) 992 usage(); 993 if ((Bind_Addr = inet_addr(optarg)) == INADDR_NONE) { 994 syslog(LOG_NOTICE, 995 "%s: invalid address", optarg); 996 usage(); 997 } 998 break; 999 case 'A': 1000 AnonFtpOnly = 1; /* restrict to anon usernames only */ 1001 break; 1002 case 'D': 1003 Debug_Level = strtol(optarg, &p, 10); 1004 if (!*optarg || *p) 1005 usage(); 1006 break; 1007 case 'g': 1008 Group = optarg; 1009 break; 1010 case 'm': 1011 min_port = strtol(optarg, &p, 10); 1012 if (!*optarg || *p) 1013 usage(); 1014 if (min_port < 0 || min_port > USHRT_MAX) 1015 usage(); 1016 break; 1017 case 'M': 1018 max_port = strtol(optarg, &p, 10); 1019 if (!*optarg || *p) 1020 usage(); 1021 if (max_port < 0 || max_port > USHRT_MAX) 1022 usage(); 1023 break; 1024 case 'n': 1025 NatMode = 1; /* pass all passives, we're using NAT */ 1026 break; 1027 case 'r': 1028 Use_Rdns = 1; /* look up hostnames */ 1029 break; 1030 case 'R': { 1031 char *s, *t; 1032 1033 if (!*optarg) 1034 usage(); 1035 if ((s = strdup(optarg)) == NULL) { 1036 syslog (LOG_NOTICE, 1037 "Insufficient memory (malloc failed)"); 1038 exit(EX_UNAVAILABLE); 1039 } 1040 memset(&real_server_sa, 0, sizeof(real_server_sa)); 1041 real_server_sa.sin_len = sizeof(struct sockaddr_in); 1042 real_server_sa.sin_family = AF_INET; 1043 t = strchr(s, ':'); 1044 if (t == NULL) 1045 real_server_sa.sin_port = htons(21); 1046 else { 1047 long port = strtol(t + 1, &p, 10); 1048 1049 if (*p || port <= 0 || port > 65535) 1050 usage(); 1051 real_server_sa.sin_port = htons(port); 1052 *t = 0; 1053 } 1054 real_server_sa.sin_addr.s_addr = inet_addr(s); 1055 if (real_server_sa.sin_addr.s_addr == INADDR_NONE) 1056 usage(); 1057 free(s); 1058 ReverseMode = 1; 1059 break; 1060 } 1061 case 'S': 1062 if (!inet_aton(optarg, &src_addr)) 1063 usage(); 1064 break; 1065 case 't': 1066 timeout_seconds = strtol(optarg, &p, 10); 1067 if (!*optarg || *p) 1068 usage(); 1069 break; 1070 case 'u': 1071 User = optarg; 1072 break; 1073 case 'V': 1074 Verbose = 1; 1075 break; 1076 #ifdef LIBWRAP 1077 case 'w': 1078 use_tcpwrapper = 1; /* do the libwrap thing */ 1079 break; 1080 #endif /* LIBWRAP */ 1081 default: 1082 usage(); 1083 /* NOTREACHED */ 1084 } 1085 } 1086 argc -= optind; 1087 argv += optind; 1088 1089 if (max_port < min_port) 1090 usage(); 1091 1092 openlog(__progname, LOG_NDELAY|LOG_PID, LOG_DAEMON); 1093 1094 setlinebuf(stdout); 1095 setlinebuf(stderr); 1096 1097 memset(&client_iob, 0, sizeof(client_iob)); 1098 memset(&server_iob, 0, sizeof(server_iob)); 1099 1100 if (get_proxy_env(0, &real_server_sa, &client_iob.sa, 1101 &proxy_sa) == -1) 1102 exit(EX_PROTOCOL); 1103 1104 /* 1105 * We may now drop root privs, as we have done our ioctl for 1106 * pf. If we do drop root, we can't make backchannel connections 1107 * for PORT and EPRT come from port 20, which is not strictly 1108 * RFC compliant. This shouldn't cause problems for all but 1109 * the stupidest ftp clients and the stupidest packet filters. 1110 */ 1111 drop_privs(); 1112 1113 /* 1114 * We check_host after get_proxy_env so that checks are done 1115 * against the original destination endpoint, not the endpoint 1116 * of our side of the rdr. This allows the use of tcpwrapper 1117 * rules to restrict destinations as well as sources of connections 1118 * for ftp. 1119 */ 1120 if (Use_Rdns) 1121 flags = 0; 1122 else 1123 flags = NI_NUMERICHOST | NI_NUMERICSERV; 1124 1125 i = getnameinfo((struct sockaddr *)&client_iob.sa, 1126 sizeof(client_iob.sa), ClientName, sizeof(ClientName), NULL, 0, 1127 flags); 1128 1129 if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) { 1130 debuglog(2, "name resolution failure (client)"); 1131 exit(EX_OSERR); 1132 } 1133 1134 i = getnameinfo((struct sockaddr *)&real_server_sa, 1135 sizeof(real_server_sa), RealServerName, sizeof(RealServerName), 1136 NULL, 0, flags); 1137 1138 if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) { 1139 debuglog(2, "name resolution failure (server)"); 1140 exit(EX_OSERR); 1141 } 1142 1143 #ifdef LIBWRAP 1144 if (use_tcpwrapper && !check_host(&client_iob.sa, &real_server_sa)) 1145 exit(EX_NOPERM); 1146 #endif 1147 1148 client_iob.fd = 0; 1149 1150 syslog(LOG_INFO, "accepted connection from %s:%u to %s:%u", ClientName, 1151 ntohs(client_iob.sa.sin_port), RealServerName, 1152 ntohs(real_server_sa.sin_port)); 1153 1154 server_iob.fd = get_backchannel_socket(SOCK_STREAM, min_port, max_port, 1155 -1, 1, &server_iob.sa); 1156 1157 if (connect(server_iob.fd, (struct sockaddr *)&real_server_sa, 1158 sizeof(real_server_sa)) != 0) { 1159 syslog(LOG_INFO, "cannot connect to %s:%u (%m)", RealServerName, 1160 ntohs(real_server_sa.sin_port)); 1161 exit(EX_NOHOST); 1162 } 1163 1164 /* 1165 * Now that we are connected to the real server, get the name 1166 * of our end of the server socket so we know our IP address 1167 * from the real server's perspective. 1168 */ 1169 salen = sizeof(server_iob.sa); 1170 getsockname(server_iob.fd, (struct sockaddr *)&server_iob.sa, &salen); 1171 1172 i = getnameinfo((struct sockaddr *)&server_iob.sa, 1173 sizeof(server_iob.sa), OurName, sizeof(OurName), NULL, 0, flags); 1174 1175 if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) { 1176 debuglog(2, "name resolution failure (local)"); 1177 exit(EX_OSERR); 1178 } 1179 1180 debuglog(1, "local socket is %s:%u", OurName, 1181 ntohs(server_iob.sa.sin_port)); 1182 1183 /* ignore SIGPIPE */ 1184 bzero(&new_sa, sizeof(new_sa)); 1185 new_sa.sa_handler = SIG_IGN; 1186 (void)sigemptyset(&new_sa.sa_mask); 1187 new_sa.sa_flags = SA_RESTART; 1188 if (sigaction(SIGPIPE, &new_sa, &old_sa) != 0) { 1189 syslog(LOG_ERR, "sigaction() failed (%m)"); 1190 exit(EX_OSERR); 1191 } 1192 1193 if (setsockopt(client_iob.fd, SOL_SOCKET, SO_OOBINLINE, (char *)&one, 1194 sizeof(one)) == -1) { 1195 syslog(LOG_NOTICE, "cannot set SO_OOBINLINE (%m)"); 1196 exit(EX_OSERR); 1197 } 1198 1199 client_iob.line_buffer_size = STARTBUFSIZE; 1200 client_iob.line_buffer = malloc(client_iob.line_buffer_size); 1201 client_iob.io_buffer_size = STARTBUFSIZE; 1202 client_iob.io_buffer = malloc(client_iob.io_buffer_size); 1203 client_iob.next_byte = 0; 1204 client_iob.io_buffer_len = 0; 1205 client_iob.alive = 1; 1206 client_iob.who = "client"; 1207 client_iob.send_oob_flags = 0; 1208 client_iob.real_sa = client_iob.sa; 1209 1210 server_iob.line_buffer_size = STARTBUFSIZE; 1211 server_iob.line_buffer = malloc(server_iob.line_buffer_size); 1212 server_iob.io_buffer_size = STARTBUFSIZE; 1213 server_iob.io_buffer = malloc(server_iob.io_buffer_size); 1214 server_iob.next_byte = 0; 1215 server_iob.io_buffer_len = 0; 1216 server_iob.alive = 1; 1217 server_iob.who = "server"; 1218 server_iob.send_oob_flags = MSG_OOB; 1219 server_iob.real_sa = real_server_sa; 1220 1221 if (client_iob.line_buffer == NULL || client_iob.io_buffer == NULL || 1222 server_iob.line_buffer == NULL || server_iob.io_buffer == NULL) { 1223 syslog (LOG_NOTICE, "insufficient memory"); 1224 exit(EX_UNAVAILABLE); 1225 } 1226 1227 while (client_iob.alive || server_iob.alive) { 1228 int maxfd = 0; 1229 fd_set *fdsp; 1230 1231 if (client_iob.fd > maxfd) 1232 maxfd = client_iob.fd; 1233 if (client_listen_socket > maxfd) 1234 maxfd = client_listen_socket; 1235 if (client_data_socket > maxfd) 1236 maxfd = client_data_socket; 1237 if (server_iob.fd > maxfd) 1238 maxfd = server_iob.fd; 1239 if (server_listen_socket > maxfd) 1240 maxfd = server_listen_socket; 1241 if (server_data_socket > maxfd) 1242 maxfd = server_data_socket; 1243 1244 debuglog(3, "client is %s; server is %s", 1245 client_iob.alive ? "alive" : "dead", 1246 server_iob.alive ? "alive" : "dead"); 1247 1248 fdsp = (fd_set *)calloc(howmany(maxfd + 1, NFDBITS), 1249 sizeof(fd_mask)); 1250 if (fdsp == NULL) { 1251 syslog(LOG_NOTICE, "insufficient memory"); 1252 exit(EX_UNAVAILABLE); 1253 } 1254 1255 if (client_iob.alive && telnet_getline(&client_iob, 1256 &server_iob)) { 1257 debuglog(3, "client line buffer is \"%s\"", 1258 (char *)client_iob.line_buffer); 1259 if (client_iob.line_buffer[0] != '\0') 1260 do_client_cmd(&client_iob, &server_iob); 1261 } else if (server_iob.alive && telnet_getline(&server_iob, 1262 &client_iob)) { 1263 debuglog(3, "server line buffer is \"%s\"", 1264 (char *)server_iob.line_buffer); 1265 if (server_iob.line_buffer[0] != '\0') 1266 do_server_reply(&server_iob, &client_iob); 1267 } else { 1268 if (client_iob.alive) { 1269 FD_SET(client_iob.fd, fdsp); 1270 if (client_listen_socket >= 0) 1271 FD_SET(client_listen_socket, fdsp); 1272 if (client_data_socket >= 0) 1273 FD_SET(client_data_socket, fdsp); 1274 } 1275 if (server_iob.alive) { 1276 FD_SET(server_iob.fd, fdsp); 1277 if (server_listen_socket >= 0) 1278 FD_SET(server_listen_socket, fdsp); 1279 if (server_data_socket >= 0) 1280 FD_SET(server_data_socket, fdsp); 1281 } 1282 tv.tv_sec = timeout_seconds; 1283 tv.tv_usec = 0; 1284 1285 doselect: 1286 sval = select(maxfd + 1, fdsp, NULL, NULL, 1287 (tv.tv_sec == 0) ? NULL : &tv); 1288 if (sval == 0) { 1289 /* 1290 * This proxy has timed out. Expire it 1291 * quietly with an obituary in the syslogs 1292 * for any passing mourners. 1293 */ 1294 syslog(LOG_INFO, 1295 "timeout: no data for %ld seconds", 1296 timeout_seconds); 1297 exit(EX_OK); 1298 } 1299 if (sval == -1) { 1300 if (errno == EINTR || errno == EAGAIN) 1301 goto doselect; 1302 syslog(LOG_NOTICE, 1303 "select() failed (%m)"); 1304 exit(EX_OSERR); 1305 } 1306 if (client_data_socket >= 0 && 1307 FD_ISSET(client_data_socket, fdsp)) { 1308 int rval; 1309 1310 debuglog(3, "transfer: client to server"); 1311 rval = xfer_data("client to server", 1312 client_data_socket, 1313 server_data_socket, 1314 client_iob.sa.sin_addr, 1315 real_server_sa.sin_addr); 1316 if (rval <= 0) { 1317 close_client_data(); 1318 close_server_data(); 1319 show_xfer_stats(); 1320 } else 1321 client_data_bytes += rval; 1322 } 1323 if (server_data_socket >= 0 && 1324 FD_ISSET(server_data_socket, fdsp)) { 1325 int rval; 1326 1327 debuglog(3, "transfer: server to client"); 1328 rval = xfer_data("server to client", 1329 server_data_socket, 1330 client_data_socket, 1331 real_server_sa.sin_addr, 1332 client_iob.sa.sin_addr); 1333 if (rval <= 0) { 1334 close_client_data(); 1335 close_server_data(); 1336 show_xfer_stats(); 1337 } else 1338 server_data_bytes += rval; 1339 } 1340 if (server_listen_socket >= 0 && 1341 FD_ISSET(server_listen_socket, fdsp)) { 1342 connect_port_backchannel(); 1343 } 1344 if (client_listen_socket >= 0 && 1345 FD_ISSET(client_listen_socket, fdsp)) { 1346 connect_pasv_backchannel(); 1347 } 1348 if (client_iob.alive && 1349 FD_ISSET(client_iob.fd, fdsp)) { 1350 client_iob.data_available = 1; 1351 } 1352 if (server_iob.alive && 1353 FD_ISSET(server_iob.fd, fdsp)) { 1354 server_iob.data_available = 1; 1355 } 1356 } 1357 free(fdsp); 1358 if (client_iob.got_eof) { 1359 shutdown(server_iob.fd, 1); 1360 shutdown(client_iob.fd, 0); 1361 client_iob.got_eof = 0; 1362 client_iob.alive = 0; 1363 } 1364 if (server_iob.got_eof) { 1365 shutdown(client_iob.fd, 1); 1366 shutdown(server_iob.fd, 0); 1367 server_iob.got_eof = 0; 1368 server_iob.alive = 0; 1369 } 1370 } 1371 1372 if (Verbose) 1373 syslog(LOG_INFO, "session ended"); 1374 1375 exit(EX_OK); 1376 } 1377