/* * Copyright (c) 1995, 1996 Gunther Schadow. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "pg_config.h" IDENT("@(#) distal.c (Gunther Schadow) 12/17/96"); #include "llpdrv.h" #include "socket.h" #include "trip.h" #include "trap.h" #include "intern.h" #include "ansi.h" #include #include #include #include #include #include #include #define NORMAL_DELAY 1 /* seconds */ /* serve_distal * * Serve the distal site, i.e. the site which connects to the line. * Bind a socket, listen and accept connections on it. When a distal * client makes a connection, claim the line and froward a message to * the line. Get the response and return it to the distal client. Shut * down the distal connection and accept again. Because there can only * be one transaction on the line, there is no forking after accept. */ void serve_distal(int proto, #ifdef USE_TRIP pid_t proxi_pid, #else semaphor mutex, #endif const char *proxi_name, int proxi_fd, int dist_af, const char *dist_addr) /* __attribute__((__noreturn__)) */ { int do_exit = 0; int server_fd; FILE *client_fp; ssignal(SIGPIPE, SIG_IGN, 0); if(dist_af != AF_INET) { syslog(LOG_ERR, "distal server will not bind AF_%d", dist_af); abort(); } server_fd = bind_inet(dist_addr); if(server_fd == FAIL) { syslog(LOG_ERR, "distal server could not bind `%s': %m", dist_addr); exit(1); } listen(server_fd, 5); /* * Setup line management handshake */ #ifdef USE_TRIP atsignal(SIGCLAIM, T_SINTR) { DBG(syslog(LOG_DEBUG, "distal server yields the line")); yield_line(proxi_pid); syslog(LOG_INFO, "distal server enters wait state"); wait_line(); } /* in initialization phase SIGYIELD is unblocked here */ atsignal(SIGYIELD, T_SINTR) { DBG(syslog(LOG_DEBUG, "distal server got SIGYIELD")); goto serve_dist; } /* The handshake is initialized by the proximal server sending * a SIGYIELD to the distal server. */ DBG(syslog(LOG_DEBUG, "distal server awaits handshake")); wait_line(); serve_dist: #endif do { int client_fd; peername_t dist_name, dist_peer; int delay = NORMAL_DELAY; syslog(LOG_INFO, "accepting at `%s'", dist_addr); client_fd = accept_inet(server_fd); if(client_fd == FAIL) { syslog(LOG_ERR,"distal accept failed: %m"); /* We will never recover from this error condition by just trying again. Something really went wrong! */ abort(); } /* timeout of initialization phase */ attimeout(30 /* seconds */ + delay * 10) { syslog(LOG_ERR, "timout during initialization"); goto fail_init; } /* * Identify client */ peername_inet(client_fd, dist_name); syslog(LOG_INFO, "distal connection to `%s' established", dist_name); /* * Make the distal socket a buffered stream. */ CHK(client_fp = fdopen(client_fd, "r+"), NULL, goto fail_init); /* * Get source address from client, but don't handle it. * Authorization procedure must check if dist_name is a * trusted host who does not cheat us with a wrong * dist_peer, and that dist_peer is in the list of hosts * that may submit requests to our proximal client. */ CHK(recv_peername(client_fp, dist_peer), FAIL, goto fail_init); /* * Only calls from `localhost' are allowed */ if(strcasecmp("localhost", dist_name) != 0) { syslog(LOG_ALERT, "unpriviledged distal client `%s' (!=localhost)", dist_name); goto fail_init; } /* Wait a while in order to give precedence to the outgoing * transactions. */ sleep(delay); delay = NORMAL_DELAY; /* release initialization timeout */ ctimeout(); /* * Claim the line. */ DBG(syslog(LOG_DEBUG, "distal server claims the line")); #ifdef USE_TRIP claim_line(proxi_pid, PRI_LOW); #else DBG( int semval; syslog(LOG_DEBUG, __FUNCTION__ ": PROBEREN"); semval = semctl(mutex->id, 0, GETVAL, 0); syslog(LOG_DEBUG, "mutex = %d", semval); ); if(PROBEREN(mutex) == FAIL) { syslog(LOG_ERR, __FUNCTION__ ":PROBEREN: %m"); exit(1); } DBG( int semval = semctl(mutex->id, 0, GETVAL, 0); syslog(LOG_DEBUG, "mutex = %d", semval); ); #endif /* * Set a gross timer, this timer should have a very long value, * since it needs to stop the process in order to recover any * dynamic allocations in the data segement. */ alarm(10 /* minutes */ * 60 /* seconds per minute */); /* * Interact depending on protocol */ switch(proto) { case P_ANSI: { ASTRM *proxi_asp; /* asattach(), i2ansi() and ansi2i() will detach the ASTRM upon FAIL */ CHK(proxi_asp = asattach(proxi_fd, 0, 0), NULL, goto fail_proximal); /* request */ CHK(i2ansi(client_fp, proxi_asp), FAIL, goto xact_fail); /* result */ CHK(ansi2i(proxi_asp, client_fp), FAIL, goto xact_fail); /* transaction successful -- now detach the ASTRM gracefully */ CHK(asdetach(proxi_asp, 1), FAIL, goto fail_proximal); } break; case P_HYBR: syslog(LOG_ERR, "hybrid protocol is not implemented"); abort(); case P_MINI: { /* since fdclose will later close the file descriptor itself, we will have fdopen act on a duplicate. */ int save_fd = dup(proxi_fd); FILE *proxi_fp; /* fdopen(), i2mini() and mini2i() will cleanup the FILE upon FAIL */ CHK(proxi_fp = fdopen(save_fd, "r+"), NULL, /* however, fdopen *might* leave the fd unclosed when it fails! */ close(save_fd); goto fail_proximal); /* request */ CHK(i2mini(client_fp, proxi_fp), FAIL, /* fclose *might* leave the fd open when it fails on fflush(){write()}! */ close(save_fd); goto xact_fail); /* result */ CHK(mini2i(proxi_fp, client_fp), FAIL, /* fclose *might* leave the fd open when it fails on fflush(){write()}! */ close(save_fd); goto xact_fail); /* transaction successful -- now close the FILE */ CHK(fclose(proxi_fp), EOF, /* fclose *might* leave the fd open when it fails on fflush(){write()}! */ close(save_fd); goto fail_proximal); } break; } syslog(LOG_INFO, "transaction finished successfully"); do_exit = 0; goto end; xact_fail: syslog(LOG_ERR, "transaction failed"); if(llp_errno == RF_EXTERN) goto fail_proximal; else { do_exit = 0; /* wait for ten seconds to recover. FIXME: AS_ERINT should wait until the proximal transaction was successful */ delay = 20 /* seconds */; goto end; } fail_proximal: syslog(LOG_ERR, "error on proximal side"); do_exit = 2; goto end; /* fail_distal: syslog(LOG_ERR, "error on distal side"); do_exit = 0; goto end; */ fail_init: ctimeout(); tcflush(proxi_fd, TCIFLUSH); syslog(LOG_ERR, "error during initialization"); CHK(fclose(client_fp), EOF, close(client_fd)); continue; end: CHK(fclose(client_fp), EOF, close(client_fd)); tcflush(proxi_fd, TCIFLUSH); alarm(0); /* release the timer */ DBG(syslog(LOG_DEBUG, "distal server yields the line")); #ifdef USE_TRIP yield_line(proxi_pid); #else DBG( int semval; syslog(LOG_DEBUG, __FUNCTION__ ": VERHOGEN"); semval = semctl(mutex->id, 0, GETVAL, 0); syslog(LOG_DEBUG, "mutex = %d", semval); ); if(VERHOGEN(mutex) == FAIL) { syslog(LOG_ERR, __FUNCTION__ ":VERHOGEN: %m"); exit(1); } DBG( int semval = semctl(mutex->id, 0, GETVAL, 0); syslog(LOG_DEBUG, "mutex = %d", semval); ); #endif } while(do_exit == 0); exit(do_exit); }