ggated.c revision 134937
1138593Ssam/*- 2138593Ssam * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3138593Ssam * All rights reserved. 4138593Ssam * 5138593Ssam * Redistribution and use in source and binary forms, with or without 6138593Ssam * modification, are permitted provided that the following conditions 7138593Ssam * are met: 8138593Ssam * 1. Redistributions of source code must retain the above copyright 9138593Ssam * notice, this list of conditions and the following disclaimer. 10138593Ssam * 2. Redistributions in binary form must reproduce the above copyright 11138593Ssam * notice, this list of conditions and the following disclaimer in the 12138593Ssam * documentation and/or other materials provided with the distribution. 13138593Ssam * 14138593Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15138593Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16138593Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17138593Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18138593Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19138593Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20138593Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21138593Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22138593Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23138593Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24138593Ssam * SUCH DAMAGE. 25138593Ssam * 26138593Ssam * $FreeBSD: head/sbin/ggate/ggated/ggated.c 134937 2004-09-08 07:57:14Z pjd $ 27138593Ssam */ 28138593Ssam 29138593Ssam#include <stdio.h> 30138593Ssam#include <stdlib.h> 31138593Ssam#include <stdint.h> 32138593Ssam#include <unistd.h> 33138593Ssam#include <fcntl.h> 34138593Ssam#include <sys/param.h> 35138593Ssam#include <sys/queue.h> 36138593Ssam#include <sys/endian.h> 37138593Ssam#include <sys/socket.h> 38138593Ssam#include <sys/ioctl.h> 39138593Ssam#include <sys/stat.h> 40138593Ssam#include <sys/time.h> 41138593Ssam#include <sys/disk.h> 42138593Ssam#include <sys/bio.h> 43138593Ssam#include <netinet/in.h> 44138593Ssam#include <netinet/tcp.h> 45138593Ssam#include <arpa/inet.h> 46138593Ssam#include <signal.h> 47138593Ssam#include <err.h> 48138593Ssam#include <errno.h> 49138593Ssam#include <string.h> 50138593Ssam#include <libgen.h> 51138593Ssam#include <syslog.h> 52138593Ssam#include <stdarg.h> 53138593Ssam 54138593Ssam#include <geom/gate/g_gate.h> 55138593Ssam#include "ggate.h" 56138593Ssam 57138593Ssam 58138593Ssam#define G_GATED_EXPORT_FILE "/etc/gg.exports" 59138593Ssam#define G_GATED_DEBUG(...) \ 60138593Ssam if (g_gate_verbose) { \ 61194799Sdelphij printf(__VA_ARGS__); \ 62194799Sdelphij printf("\n"); \ 63138593Ssam } 64138593Ssam 65138593Ssamstatic const char *exports = G_GATED_EXPORT_FILE; 66138593Ssamstatic int got_sighup = 0; 67138593Ssamstatic int nagle = 1; 68138593Ssamstatic unsigned rcvbuf = G_GATE_RCVBUF; 69138593Ssamstatic unsigned sndbuf = G_GATE_SNDBUF; 70197138Shrs 71197138Shrsstruct export { 72222711Shrs char *e_path; /* path to device/file */ 73197138Shrs in_addr_t e_ip; /* remote IP address */ 74138593Ssam in_addr_t e_mask; /* IP mask */ 75138593Ssam unsigned e_flags; /* flags (RO/RW) */ 76138593Ssam SLIST_ENTRY(export) e_next; 77138593Ssam}; 78138593Ssamstatic SLIST_HEAD(, export) exports_list = 79138593Ssam SLIST_HEAD_INITIALIZER(&exports_list); 80138593Ssam 81138593Ssamstatic void 82138593Ssamusage(void) 83138593Ssam{ 84138593Ssam 85138593Ssam fprintf(stderr, "usage: %s [-nv] [-a address] [-p port] [-R rcvbuf] " 86138593Ssam "[-S sndbuf] [exports file]\n", getprogname()); 87138593Ssam exit(EXIT_FAILURE); 88138593Ssam} 89138593Ssam 90138593Ssamstatic char * 91138593Ssamip2str(in_addr_t ip) 92138593Ssam{ 93138593Ssam static char sip[16]; 94138593Ssam 95138593Ssam snprintf(sip, sizeof(sip), "%u.%u.%u.%u", 96138593Ssam ((ip >> 24) & 0xff), 97138593Ssam ((ip >> 16) & 0xff), 98138593Ssam ((ip >> 8) & 0xff), 99138593Ssam (ip & 0xff)); 100138593Ssam return (sip); 101138593Ssam} 102138593Ssam 103138593Ssamstatic in_addr_t 104138593Ssamcountmask(unsigned m) 105138593Ssam{ 106138593Ssam in_addr_t mask; 107138593Ssam 108138593Ssam if (m == 0) { 109138593Ssam mask = 0x0; 110138593Ssam } else { 111138593Ssam mask = 1 << (32 - m); 112138593Ssam mask--; 113138593Ssam mask = ~mask; 114138593Ssam } 115138593Ssam return (mask); 116138593Ssam} 117138593Ssam 118138593Ssamstatic void 119138593Ssamline_parse(char *line, unsigned lineno) 120138593Ssam{ 121138593Ssam struct export *ex; 122138593Ssam char *word, *path, *sflags; 123138593Ssam unsigned flags, i, vmask; 124138593Ssam in_addr_t ip, mask; 125138593Ssam 126138593Ssam ip = mask = flags = vmask = 0; 127138593Ssam path = NULL; 128138593Ssam sflags = NULL; 129138593Ssam 130138593Ssam for (i = 0, word = strtok(line, " \t"); word != NULL; 131138593Ssam i++, word = strtok(NULL, " \t")) { 132138593Ssam switch (i) { 133138593Ssam case 0: /* IP address or host name */ 134138593Ssam ip = g_gate_str2ip(strsep(&word, "/")); 135138593Ssam if (ip == INADDR_NONE) { 136138593Ssam g_gate_xlog("Invalid IP/host name at line %u.", 137138593Ssam lineno); 138138593Ssam } 139138593Ssam ip = ntohl(ip); 140138593Ssam if (word == NULL) 141138593Ssam vmask = 32; 142138593Ssam else { 143138593Ssam errno = 0; 144138593Ssam vmask = strtoul(word, NULL, 10); 145138593Ssam if (vmask == 0 && errno != 0) { 146138593Ssam g_gate_xlog("Invalid IP mask value at " 147138593Ssam "line %u.", lineno); 148138593Ssam } 149138593Ssam if ((unsigned)vmask > 32) { 150138593Ssam g_gate_xlog("Invalid IP mask value at line %u.", 151138593Ssam lineno); 152138593Ssam } 153138593Ssam } 154138593Ssam mask = countmask(vmask); 155138593Ssam break; 156138593Ssam case 1: /* flags */ 157138593Ssam if (strcasecmp("rd", word) == 0 || 158138593Ssam strcasecmp("ro", word) == 0) { 159138593Ssam flags = O_RDONLY; 160138593Ssam } else if (strcasecmp("wo", word) == 0) { 161138593Ssam flags = O_WRONLY; 162138593Ssam } else if (strcasecmp("rw", word) == 0) { 163138593Ssam flags = O_RDWR; 164138593Ssam } else { 165138593Ssam g_gate_xlog("Invalid value in flags field at " 166138593Ssam "line %u.", lineno); 167138593Ssam } 168138593Ssam sflags = word; 169138593Ssam break; 170138593Ssam case 2: /* path */ 171138593Ssam if (strlen(word) >= MAXPATHLEN) { 172138593Ssam g_gate_xlog("Path too long at line %u. ", 173138593Ssam lineno); 174138593Ssam } 175138593Ssam path = word; 176138593Ssam break; 177138593Ssam default: 178138593Ssam g_gate_xlog("Too many arguments at line %u. ", lineno); 179138593Ssam } 180138593Ssam } 181166956Ssam if (i != 3) 182138593Ssam g_gate_xlog("Too few arguments at line %u.", lineno); 183138593Ssam 184138593Ssam ex = malloc(sizeof(*ex)); 185138593Ssam if (ex == NULL) 186138593Ssam g_gate_xlog("No enough memory."); 187138593Ssam ex->e_path = strdup(path); 188138593Ssam if (ex->e_path == NULL) 189138593Ssam g_gate_xlog("No enough memory."); 190138593Ssam 191138593Ssam /* Made 'and' here. */ 192138593Ssam ex->e_ip = (ip & mask); 193138593Ssam ex->e_mask = mask; 194166956Ssam ex->e_flags = flags; 195138593Ssam 196138593Ssam SLIST_INSERT_HEAD(&exports_list, ex, e_next); 197138593Ssam 198138593Ssam g_gate_log(LOG_DEBUG, "Added %s/%u %s %s to exports list.", 199138593Ssam ip2str(ex->e_ip), vmask, path, sflags); 200138593Ssam} 201138593Ssam 202138593Ssamstatic void 203138593Ssamexports_clear(void) 204138593Ssam{ 205138593Ssam struct export *ex; 206138593Ssam 207138593Ssam while (!SLIST_EMPTY(&exports_list)) { 208138593Ssam ex = SLIST_FIRST(&exports_list); 209138593Ssam SLIST_REMOVE_HEAD(&exports_list, e_next); 210138593Ssam free(ex); 211138593Ssam } 212138593Ssam} 213138593Ssam 214138593Ssam#define EXPORTS_LINE_SIZE 2048 215138593Ssamstatic void 216138593Ssamexports_get(void) 217138593Ssam{ 218138593Ssam char buf[EXPORTS_LINE_SIZE], *line; 219138593Ssam unsigned lineno = 0, objs = 0, len; 220138593Ssam FILE *fd; 221138593Ssam 222138593Ssam exports_clear(); 223138593Ssam 224138593Ssam fd = fopen(exports, "r"); 225138593Ssam if (fd == NULL) { 226138593Ssam g_gate_xlog("Cannot open exports file (%s): %s.", exports, 227138593Ssam strerror(errno)); 228138593Ssam } 229138593Ssam 230138593Ssam g_gate_log(LOG_INFO, "Reading exports file (%s).", exports); 231138593Ssam 232138593Ssam for (;;) { 233146187Sume if (fgets(buf, sizeof(buf), fd) == NULL) { 234138593Ssam if (feof(fd)) 235138593Ssam break; 236138593Ssam 237138593Ssam g_gate_xlog("Error while reading exports file: %s.", 238138593Ssam strerror(errno)); 239166956Ssam } 240166956Ssam 241138593Ssam /* Increase line count. */ 242138593Ssam lineno++; 243138593Ssam 244138593Ssam /* Skip spaces and tabs. */ 245166956Ssam for (line = buf; *line == ' ' || *line == '\t'; ++line) 246138593Ssam ; 247138593Ssam 248138593Ssam /* Empty line, comment or empty line at the end of file. */ 249138593Ssam if (*line == '\n' || *line == '#' || *line == '\0') 250138593Ssam continue; 251138593Ssam 252138593Ssam len = strlen(line); 253138593Ssam if (line[len - 1] == '\n') { 254138593Ssam /* Remove new line char. */ 255138593Ssam line[len - 1] = '\0'; 256138593Ssam } else { 257138593Ssam if (!feof(fd)) 258138593Ssam g_gate_xlog("Line %u too long.", lineno); 259138593Ssam } 260138593Ssam 261138593Ssam line_parse(line, lineno); 262146187Sume objs++; 263138593Ssam } 264138593Ssam 265138593Ssam fclose(fd); 266138593Ssam 267138593Ssam if (objs == 0) 268138593Ssam g_gate_xlog("There are no objects to export."); 269138593Ssam 270166956Ssam g_gate_log(LOG_INFO, "Exporting %u object(s).", objs); 271166956Ssam} 272138593Ssam 273138593Ssamstatic struct export * 274138593Ssamexports_find(struct sockaddr *s, const char *path) 275138593Ssam{ 276138593Ssam struct export *ex; 277138593Ssam in_addr_t ip; 278138593Ssam 279138593Ssam ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); 280138593Ssam SLIST_FOREACH(ex, &exports_list, e_next) { 281138593Ssam if ((ip & ex->e_mask) != ex->e_ip) 282138593Ssam continue; 283138593Ssam if (path != NULL && strcmp(path, ex->e_path) != 0) 284138593Ssam continue; 285138593Ssam 286138593Ssam g_gate_log(LOG_INFO, "Connection from: %s.", ip2str(ip)); 287138593Ssam return (ex); 288138593Ssam } 289138593Ssam g_gate_log(LOG_INFO, "Unauthorized connection from: %s.", ip2str(ip)); 290138593Ssam 291138593Ssam return (NULL); 292138593Ssam} 293138593Ssam 294138593Ssamstatic void 295138593Ssamsendfail(int sfd, int error, const char *fmt, ...) 296138593Ssam{ 297138593Ssam struct g_gate_sinit sinit; 298138593Ssam va_list ap; 299138593Ssam int data; 300138593Ssam 301138593Ssam sinit.gs_error = error; 302138593Ssam g_gate_swap2n_sinit(&sinit); 303138593Ssam data = send(sfd, &sinit, sizeof(sinit), 0); 304138593Ssam g_gate_swap2h_sinit(&sinit); 305138593Ssam if (data == -1) { 306138593Ssam g_gate_xlog("Error while sending initial packet: %s.", 307138593Ssam strerror(errno)); 308138593Ssam } 309138593Ssam if (fmt != NULL) { 310138593Ssam va_start(ap, fmt); 311138593Ssam g_gate_xvlog(fmt, ap); 312138593Ssam /* NOTREACHED */ 313138593Ssam va_end(ap); 314138593Ssam } 315138593Ssam exit(EXIT_FAILURE); 316138593Ssam} 317138593Ssam 318138593Ssamstatic void 319138593Ssamserve(int sfd, struct sockaddr *s) 320138593Ssam{ 321138593Ssam struct g_gate_cinit cinit; 322138593Ssam struct g_gate_sinit sinit; 323138593Ssam struct g_gate_hdr hdr; 324138593Ssam struct export *ex; 325138593Ssam char ipmask[32]; /* 32 == strlen("xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx")+1 */ 326138593Ssam size_t bufsize; 327138593Ssam int32_t error; 328138593Ssam int fd, flags; 329138593Ssam ssize_t data; 330138593Ssam char *buf; 331138593Ssam 332138593Ssam g_gate_log(LOG_DEBUG, "Receiving initial packet."); 333138593Ssam data = recv(sfd, &cinit, sizeof(cinit), MSG_WAITALL); 334138593Ssam g_gate_swap2h_cinit(&cinit); 335138593Ssam if (data == -1) { 336138593Ssam g_gate_xlog("Error while receiving initial packet: %s.", 337138593Ssam strerror(errno)); 338138593Ssam } 339138593Ssam 340138593Ssam ex = exports_find(s, cinit.gc_path); 341138593Ssam if (ex == NULL) { 342138593Ssam sendfail(sfd, EINVAL, "Requested path isn't exported: %s.", 343138593Ssam strerror(errno)); 344138593Ssam } 345138593Ssam 346138593Ssam error = 0; 347138593Ssam strlcpy(ipmask, ip2str(ex->e_ip), sizeof(ipmask)); 348138593Ssam strlcat(ipmask, "/", sizeof(ipmask)); 349138593Ssam strlcat(ipmask, ip2str(ex->e_mask), sizeof(ipmask)); 350138593Ssam if ((cinit.gc_flags & G_GATE_FLAG_READONLY) != 0) { 351138593Ssam if (ex->e_flags == O_WRONLY) { 352138593Ssam g_gate_log(LOG_ERR, "Read-only access requested, but " 353138593Ssam "%s (%s) is exported write-only.", ex->e_path, 354138593Ssam ipmask); 355138593Ssam error = EPERM; 356138593Ssam } else { 357138593Ssam sinit.gs_flags = G_GATE_FLAG_READONLY; 358138593Ssam } 359138593Ssam } else if ((cinit.gc_flags & G_GATE_FLAG_WRITEONLY) != 0) { 360138593Ssam if (ex->e_flags == O_RDONLY) { 361138593Ssam g_gate_log(LOG_ERR, "Write-only access requested, but " 362138593Ssam "%s (%s) is exported read-only.", ex->e_path, 363138593Ssam ipmask); 364138593Ssam error = EPERM; 365138593Ssam } else { 366138593Ssam sinit.gs_flags = G_GATE_FLAG_WRITEONLY; 367138593Ssam } 368138593Ssam } else { 369138593Ssam if (ex->e_flags == O_RDONLY) { 370138593Ssam g_gate_log(LOG_ERR, "Read-write access requested, but " 371138593Ssam "%s (%s) is exported read-only.", ex->e_path, 372138593Ssam ipmask); 373138593Ssam error = EPERM; 374138593Ssam } else if (ex->e_flags == O_WRONLY) { 375138593Ssam g_gate_log(LOG_ERR, "Read-write access requested, but " 376138593Ssam "%s (%s) is exported write-only.", ex->e_path, 377138593Ssam ipmask); 378138593Ssam error = EPERM; 379138593Ssam } else { 380138593Ssam sinit.gs_flags = 0; 381138593Ssam } 382138593Ssam } 383138593Ssam if (error != 0) 384138593Ssam sendfail(sfd, error, NULL); 385138593Ssam flags = g_gate_openflags(sinit.gs_flags); 386138593Ssam fd = open(ex->e_path, flags); 387138593Ssam if (fd < 0) { 388138593Ssam sendfail(sfd, errno, "Error while opening %s: %s.", ex->e_path, 389138593Ssam strerror(errno)); 390138593Ssam } 391138593Ssam 392138593Ssam g_gate_log(LOG_DEBUG, "Sending initial packet."); 393138593Ssam /* 394138593Ssam * This field isn't used by ggc(8) for now. 395138593Ssam * It should be used in future when user don't give device size. 396138593Ssam */ 397138593Ssam sinit.gs_mediasize = g_gate_mediasize(fd); 398138593Ssam sinit.gs_sectorsize = g_gate_sectorsize(fd); 399138593Ssam sinit.gs_error = 0; 400138593Ssam g_gate_swap2n_sinit(&sinit); 401138593Ssam data = send(sfd, &sinit, sizeof(sinit), 0); 402138593Ssam g_gate_swap2h_sinit(&sinit); 403138593Ssam if (data == -1) { 404138593Ssam sendfail(sfd, errno, "Error while sending initial packet: %s.", 405138593Ssam strerror(errno)); 406138593Ssam } 407138593Ssam 408138593Ssam bufsize = G_GATE_BUFSIZE_START; 409138593Ssam buf = malloc(bufsize); 410138593Ssam if (buf == NULL) 411138593Ssam g_gate_xlog("No enough memory."); 412138593Ssam 413138593Ssam g_gate_log(LOG_DEBUG, "New process: %u.", getpid()); 414138593Ssam 415138593Ssam for (;;) { 416138593Ssam /* 417138593Ssam * Receive request. 418138593Ssam */ 419138593Ssam data = recv(sfd, &hdr, sizeof(hdr), MSG_WAITALL); 420138593Ssam if (data == 0) { 421138593Ssam g_gate_log(LOG_DEBUG, "Process %u exiting.", getpid()); 422138593Ssam exit(EXIT_SUCCESS); 423138593Ssam } else if (data == -1) { 424138593Ssam g_gate_xlog("Error while receiving hdr packet: %s.", 425138593Ssam strerror(errno)); 426138593Ssam } else if (data != sizeof(hdr)) { 427138593Ssam g_gate_xlog("Malformed hdr packet received."); 428138593Ssam } 429138593Ssam g_gate_log(LOG_DEBUG, "Received hdr packet."); 430138593Ssam g_gate_swap2h_hdr(&hdr); 431138593Ssam 432138593Ssam /* 433138593Ssam * Increase buffer if there is need to. 434138593Ssam */ 435138593Ssam if (hdr.gh_length > bufsize) { 436138593Ssam bufsize = hdr.gh_length; 437138593Ssam g_gate_log(LOG_DEBUG, "Increasing buffer to %u.", 438138593Ssam bufsize); 439138593Ssam buf = realloc(buf, bufsize); 440138593Ssam if (buf == NULL) 441138593Ssam g_gate_xlog("No enough memory."); 442138593Ssam } 443138593Ssam 444138593Ssam if (hdr.gh_cmd == BIO_READ) { 445138593Ssam if (pread(fd, buf, hdr.gh_length, 446138593Ssam hdr.gh_offset) == -1) { 447138593Ssam error = errno; 448138593Ssam g_gate_log(LOG_ERR, "Error while reading data " 449138593Ssam "(offset=%ju, size=%zu): %s.", 450138593Ssam (uintmax_t)hdr.gh_offset, 451138593Ssam (size_t)hdr.gh_length, strerror(error)); 452138593Ssam } else { 453138593Ssam error = 0; 454138593Ssam } 455138593Ssam hdr.gh_error = error; 456138593Ssam g_gate_swap2n_hdr(&hdr); 457147437Sume if (send(sfd, &hdr, sizeof(hdr), 0) == -1) { 458147437Sume g_gate_xlog("Error while sending status: %s.", 459147437Sume strerror(errno)); 460146187Sume } 461146187Sume g_gate_swap2h_hdr(&hdr); 462138593Ssam /* Send data only if there was no error while pread(). */ 463138593Ssam if (error == 0) { 464138593Ssam data = send(sfd, buf, hdr.gh_length, 0); 465138593Ssam if (data == -1) { 466147437Sume g_gate_xlog("Error while sending data: " 467147437Sume "%s.", strerror(errno)); 468147437Sume } 469146187Sume g_gate_log(LOG_DEBUG, "Sent %d bytes " 470146187Sume "(offset=%ju, size=%zu).", data, 471138593Ssam (uintmax_t)hdr.gh_offset, 472138593Ssam (size_t)hdr.gh_length); 473138593Ssam } 474138593Ssam } else /* if (hdr.gh_cmd == BIO_WRITE) */ { 475138593Ssam g_gate_log(LOG_DEBUG, "Waiting for %u bytes of data...", 476138593Ssam hdr.gh_length); 477138593Ssam data = recv(sfd, buf, hdr.gh_length, MSG_WAITALL); 478138593Ssam if (data == -1) { 479138593Ssam g_gate_xlog("Error while receiving data: %s.", 480138593Ssam strerror(errno)); 481138593Ssam } 482138593Ssam if (pwrite(fd, buf, hdr.gh_length, hdr.gh_offset) == -1) { 483138593Ssam error = errno; 484138593Ssam g_gate_log(LOG_ERR, "Error while writing data " 485138593Ssam "(offset=%llu, size=%u): %s.", 486138593Ssam hdr.gh_offset, hdr.gh_length, 487138593Ssam strerror(error)); 488138593Ssam } else { 489138593Ssam error = 0; 490138593Ssam } 491138593Ssam hdr.gh_error = error; 492138593Ssam g_gate_swap2n_hdr(&hdr); 493138593Ssam if (send(sfd, &hdr, sizeof(hdr), 0) == -1) { 494138593Ssam g_gate_xlog("Error while sending status: %s.", 495138593Ssam strerror(errno)); 496138593Ssam } 497138593Ssam g_gate_swap2h_hdr(&hdr); 498138593Ssam g_gate_log(LOG_DEBUG, "Received %d bytes (offset=%llu, " 499138593Ssam "size=%u).", data, hdr.gh_offset, hdr.gh_length); 500197138Shrs } 501197138Shrs g_gate_log(LOG_DEBUG, "Tick."); 502222728Shrs } 503222728Shrs} 504197138Shrs 505197138Shrsstatic void 506197138Shrshuphandler(int sig __unused) 507197138Shrs{ 508197138Shrs 509197138Shrs got_sighup = 1; 510197138Shrs} 511197138Shrs 512197138Shrsint 513197138Shrsmain(int argc, char *argv[]) 514138593Ssam{ 515138593Ssam struct sockaddr_in serv; 516138593Ssam struct sockaddr from; 517138593Ssam in_addr_t bindaddr; 518138593Ssam socklen_t fromlen; 519138593Ssam struct timeval tv; 520138593Ssam int on, sfd, tmpsfd; 521138593Ssam pid_t childpid; 522138593Ssam unsigned bsize, port; 523138593Ssam 524138593Ssam bindaddr = htonl(INADDR_ANY); 525222711Shrs port = G_GATE_PORT; 526138593Ssam for (;;) { 527138593Ssam int ch; 528138593Ssam 529138593Ssam ch = getopt(argc, argv, "a:hnp:R:S:v"); 530138593Ssam if (ch == -1) 531166446Sbms break; 532138593Ssam switch (ch) { 533138593Ssam case 'a': 534138593Ssam bindaddr = g_gate_str2ip(optarg); 535138593Ssam if (bindaddr == INADDR_NONE) { 536138593Ssam errx(EXIT_FAILURE, 537138593Ssam "Invalid IP/host name to bind to."); 538138593Ssam } 539138593Ssam break; 540194799Sdelphij case 'n': 541138593Ssam nagle = 0; 542138593Ssam break; 543138593Ssam case 'p': 544138593Ssam errno = 0; 545138593Ssam port = strtoul(optarg, NULL, 10); 546194799Sdelphij if (port == 0 && errno != 0) 547138593Ssam errx(EXIT_FAILURE, "Invalid port."); 548224179Sbz break; 549222527Sbz case 'R': 550222527Sbz errno = 0; 551224179Sbz rcvbuf = strtoul(optarg, NULL, 10); 552222527Sbz if (rcvbuf == 0 && errno != 0) 553138593Ssam errx(EXIT_FAILURE, "Invalid rcvbuf."); 554138593Ssam break; 555138593Ssam case 'S': 556138593Ssam errno = 0; 557138593Ssam sndbuf = strtoul(optarg, NULL, 10); 558138593Ssam if (sndbuf == 0 && errno != 0) 559 errx(EXIT_FAILURE, "Invalid sndbuf."); 560 break; 561 case 'v': 562 g_gate_verbose++; 563 break; 564 case 'h': 565 default: 566 usage(); 567 } 568 } 569 argc -= optind; 570 argv += optind; 571 572 if (argv[0] != NULL) 573 exports = argv[0]; 574 exports_get(); 575 576 if (!g_gate_verbose) { 577 /* Run in daemon mode. */ 578 if (daemon(0, 0) == -1) 579 g_gate_xlog("Can't daemonize: %s", strerror(errno)); 580 } 581 582 signal(SIGCHLD, SIG_IGN); 583 584 sfd = socket(AF_INET, SOCK_STREAM, 0); 585 if (sfd == -1) 586 g_gate_xlog("Can't open stream socket: %s.", strerror(errno)); 587 bzero(&serv, sizeof(serv)); 588 serv.sin_family = AF_INET; 589 serv.sin_addr.s_addr = bindaddr; 590 serv.sin_port = htons(port); 591 on = 1; 592 if (nagle) { 593 if (setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, &on, 594 sizeof(on)) == -1) { 595 g_gate_xlog("setsockopt() error: %s.", strerror(errno)); 596 } 597 } 598 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) 599 g_gate_xlog("setsockopt(): %s.", strerror(errno)); 600 bsize = rcvbuf; 601 if (setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, &bsize, sizeof(bsize)) == -1) 602 g_gate_xlog("setsockopt(): %s.", strerror(errno)); 603 bsize = sndbuf; 604 if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, &bsize, sizeof(bsize)) == -1) 605 g_gate_xlog("setsockopt(): %s.", strerror(errno)); 606 tv.tv_sec = 10; 607 tv.tv_usec = 0; 608 if (setsockopt(sfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1 || 609 setsockopt(sfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { 610 g_gate_xlog("setsockopt() error: %s.", strerror(errno)); 611 } 612 if (bind(sfd, (struct sockaddr *)&serv, sizeof(serv)) == -1) 613 g_gate_xlog("bind(): %s.", strerror(errno)); 614 if (listen(sfd, 5) == -1) 615 g_gate_xlog("listen(): %s.", strerror(errno)); 616 617 g_gate_log(LOG_INFO, "Listen on port: %d.", port); 618 619 signal(SIGHUP, huphandler); 620 621 for (;;) { 622 fromlen = sizeof(from); 623 tmpsfd = accept(sfd, &from, &fromlen); 624 if (tmpsfd == -1) 625 g_gate_xlog("accept(): %s.", strerror(errno)); 626 627 if (got_sighup) { 628 got_sighup = 0; 629 exports_get(); 630 } 631 632 if (exports_find(&from, NULL) == NULL) { 633 close(tmpsfd); 634 continue; 635 } 636 637 childpid = fork(); 638 if (childpid < 0) { 639 g_gate_xlog("Cannot create child process: %s.", 640 strerror(errno)); 641 } else if (childpid == 0) { 642 close(sfd); 643 serve(tmpsfd, &from); 644 /* NOTREACHED */ 645 } 646 close(tmpsfd); 647 } 648 close(sfd); 649 exit(EXIT_SUCCESS); 650} 651