示例#1
0
文件: pptpctrl.c 项目: AllardJ/Tomato
/*
 * pptp_handle_ctrl_connection
 *
 * 1. read a packet (should be start_ctrl_conn_rqst)
 * 2. reply to packet (send a start_ctrl_conn_rply)
 * 3. proceed with GRE and CTRL connections
 *
 * args: pppaddrs - ppp local and remote addresses (strings)
 *       inetaddrs - local and client socket address
 * retn: 0 success, -1 failure
 */
static void pptp_handle_ctrl_connection(char **pppaddrs, struct in_addr *inetaddrs)
{

	/* For echo requests used to check link is alive */
	int echo_wait = FALSE;		/* Waiting for echo? */
	u_int32_t echo_count = 0;	/* Sequence # of echo */
	time_t echo_time = 0;		/* Time last echo req sent */
	struct timeval idleTime;	/* How long to select() */

	/* General local variables */
	ssize_t rply_size;		/* Reply packet size */
	fd_set fds;			/* For select() */
	int maxfd = clientSocket;	/* For select() */
	int send_packet;		/* Send a packet this time? */
#if BSDUSER_PPP || SLIRP
/* not needed by stuff which uses socketpair() in startCall() */
#define init 1
#else
	int init = 0;			/* Has pppd initialized the pty? */
#endif
	int pty_fd = -1;		/* File descriptor of pty */
	int gre_fd = -1;		/* Network file descriptor */
	int sig_fd = sigpipe_fd();	/* Signal pipe descriptor	*/

	unsigned char packet[PPTP_MAX_CTRL_PCKT_SIZE];
	unsigned char rply_packet[PPTP_MAX_CTRL_PCKT_SIZE];

	for (;;) {

		FD_ZERO(&fds);
		FD_SET(sig_fd, &fds);
		FD_SET(clientSocket, &fds);
		if (pty_fd != -1)
			FD_SET(pty_fd, &fds);
		if (gre_fd != -1 && init)
			FD_SET(gre_fd, &fds);

		/* set timeout */
		if (encaps_gre(-1, NULL, 0) || decaps_hdlc(-1, NULL, 0)) {
			idleTime.tv_sec = 0;
			idleTime.tv_usec = 50000; /* don't ack immediately */
		} else {
			idleTime.tv_sec = IDLE_WAIT;
			idleTime.tv_usec = 0;
		}

		/* default: do nothing */
		send_packet = FALSE;

		switch (select(maxfd + 1, &fds, NULL, NULL, &idleTime)) {
		case -1:	/* Error with select() */
			if (errno != EINTR)
				syslog(LOG_ERR, "CTRL: Error with select(), quitting");
			goto leave_clear_call;

		case 0:
			if (decaps_hdlc(-1, NULL, 0)) {
				if(decaps_hdlc(-1, encaps_gre, gre_fd))
					syslog(LOG_ERR, "CTRL: GRE re-xmit failed");
			} else if (encaps_gre(-1, NULL, 0))
				/* Pending ack and nothing else to do */
				encaps_gre(gre_fd, NULL, 0);	/* send ack with no payload */
			else if (echo_wait != TRUE) {
				/* Timeout. Start idle link detection. */
				echo_count++;
				if (pptpctrl_debug)
					syslog(LOG_DEBUG, "CTRL: Sending ECHO REQ id %d", echo_count);
				time(&echo_time);
				make_echo_req_packet(rply_packet, &rply_size, echo_count);
				echo_wait = TRUE;
				send_packet = TRUE;
			}
			break;

		default:
			break;
		}

		/* check for pending SIGTERM delivery */
		if (FD_ISSET(sig_fd, &fds)) {
			if (sigpipe_read() == SIGTERM)
				bail(SIGTERM);
		}

		/* detect startup of pppd */
#ifndef init
		if (!init && pty_fd != -1 && FD_ISSET(pty_fd, &fds))
			init = 1;
#endif

		/* handle actual packets */

		/* send from pty off via GRE */
		if (pty_fd != -1 && FD_ISSET(pty_fd, &fds) && decaps_hdlc(pty_fd, encaps_gre, gre_fd) < 0) {
			syslog(LOG_ERR, "CTRL: PTY read or GRE write failed (pty,gre)=(%d,%d)", pty_fd, gre_fd);
			break;
		}
		/* send from GRE off to pty */
		if (gre_fd != -1 && FD_ISSET(gre_fd, &fds) && decaps_gre(gre_fd, encaps_hdlc, pty_fd) < 0) {
			if (gre_fd == 6 && pty_fd == 5) {
				syslog(LOG_ERR, "CTRL: GRE-tunnel has collapsed (GRE read or PTY write failed (gre,pty)=(%d,%d))", gre_fd, pty_fd);
			} else {
				syslog(LOG_ERR, "CTRL: GRE read or PTY write failed (gre,pty)=(%d,%d)", gre_fd, pty_fd);
			}
			break;
		}
		/* handle control messages */

		if (FD_ISSET(clientSocket, &fds)) {
			send_packet = TRUE;
			switch (read_pptp_packet(clientSocket, packet, rply_packet, &rply_size)) {
			case 0:
				syslog(LOG_ERR, "CTRL: CTRL read failed");
				goto leave_drop_call;

			case -1:
				send_packet = FALSE;
				break;

			case STOP_CTRL_CONN_RQST:
				if (pptpctrl_debug)
					syslog(LOG_DEBUG, "CTRL: Received STOP CTRL CONN request (disconnecting)");
				if (gre_fd != -1 || pty_fd != -1)
					syslog(LOG_WARNING, "CTRL: Request to close control connection when call is open, closing");
				send_pptp_packet(clientSocket, rply_packet, rply_size);
				goto leave_drop_call;

			case CALL_CLR_RQST:
				if(pptpctrl_debug)
					syslog(LOG_DEBUG, "CTRL: Received CALL CLR request (closing call)");
				if (gre_fd == -1 || pty_fd == -1)
					syslog(LOG_WARNING, "CTRL: Request to close call but call not open");
				if (gre_fd != -1) {
					FD_CLR(gre_fd, &fds);
					close(gre_fd);
					gre_fd = -1;
				}
				if (pty_fd != -1) {
					FD_CLR(pty_fd, &fds);
					close(pty_fd);
					pty_fd = -1;
				}
				/* violating RFC */
                                goto leave_drop_call;

			case OUT_CALL_RQST:
				/* for killing off the link later (ugly) */
				NOTE_VALUE(PAC, call_id_pair, ((struct pptp_out_call_rply *) (rply_packet))->call_id);
				NOTE_VALUE(PNS, call_id_pair, ((struct pptp_out_call_rply *) (rply_packet))->call_id_peer);
				if (gre_fd != -1 || pty_fd != -1) {
					syslog(LOG_WARNING, "CTRL: Request to open call when call is already open, closing");
					if (gre_fd != -1) {
						FD_CLR(gre_fd, &fds);
						close(gre_fd);
						gre_fd = -1;
					}
					if (pty_fd != -1) {
						FD_CLR(pty_fd, &fds);
						close(pty_fd);
						pty_fd = -1;
					}
				}
                                /* change process title for accounting and status scripts */
                                my_setproctitle(gargc, gargv,
                                      "pptpd [%s:%04X - %04X]",
                                      inet_ntoa(inetaddrs[1]),
                                      ntohs(((struct pptp_out_call_rply *) (rply_packet))->call_id_peer),
                                      ntohs(((struct pptp_out_call_rply *) (rply_packet))->call_id));
				/* start the call, by launching pppd */
				syslog(LOG_INFO, "CTRL: Starting call (launching pppd, opening GRE)");
				pty_fd = startCall(pppaddrs, inetaddrs);
				if (pty_fd > maxfd) maxfd = pty_fd;
				if ((gre_fd = pptp_gre_init(call_id_pair, pty_fd, inetaddrs)) > maxfd)
					maxfd = gre_fd;
				break;

			case ECHO_RPLY:
				if (echo_wait == TRUE && ((struct pptp_echo_rply *) (packet))->identifier == echo_count)
					echo_wait = FALSE;
				else
					syslog(LOG_WARNING, "CTRL: Unexpected ECHO REPLY packet");
				/* FALLTHRU */
			case SET_LINK_INFO:
				send_packet = FALSE;
				break;

#ifdef PNS_MODE
			case IN_CALL_RQST:
			case IN_CALL_RPLY:
			case IN_CALL_CONN:
#endif

			case CALL_DISCONN_NTFY:
			case STOP_CTRL_CONN_RPLY:
				/* These don't generate replies.  Also they come from things we don't send in this section. */
				syslog(LOG_WARNING, "CTRL: Got a reply to a packet we didn't send");
				send_packet = FALSE;
				break;

			/* Otherwise, the already-formed reply will do fine, so send it */
			}
		}

		/* send reply packet - this may block, but it should be very rare */
		if (send_packet == TRUE && send_pptp_packet(clientSocket, rply_packet, rply_size) < 0) {
			syslog(LOG_ERR, "CTRL: Error sending GRE, aborting call");
			goto leave_clear_call;
		}

		/* waiting for echo reply and curtime - echo_time > max wait */
		if (echo_wait == TRUE && (time(NULL) - echo_time) > MAX_ECHO_WAIT) {
			syslog(LOG_INFO, "CTRL: Session timed out, ending call");
			goto leave_clear_call;
		}
	}
	/* Finished! :-) */
leave_drop_call:
	NOTE_VALUE(PAC, call_id_pair, htons(-1));
	NOTE_VALUE(PNS, call_id_pair, htons(-1));
	close(clientSocket);
leave_clear_call:
	/* leave clientSocket and call_id_pair alone for bail() */
	if (gre_fd != -1)
		close(gre_fd);
	gre_fd = -1;
	if (pty_fd != -1)
		close(pty_fd);
	pty_fd = -1;
	return;
#ifdef init
#undef init
#endif
}
示例#2
0
/*** pptp_gre_copy ************************************************************/
void pptp_gre_copy(u_int16_t call_id, u_int16_t peer_call_id, 
		   int pty_fd, int gre_fd)
{
    int max_fd;
    pptp_gre_call_id = call_id;
    pptp_gre_peer_call_id = peer_call_id;
    /* Pseudo-terminal already open. */
    ack_sent = ack_recv = seq_sent = seq_recv = 0;
    /* weird select semantics */
    max_fd = gre_fd;
    if (pty_fd > max_fd) max_fd = pty_fd;
    /* Dispatch loop */
    for (;;) { /* until error happens on gre_fd or pty_fd */
        struct timeval tv = {0, 0};
        struct timeval *tvp;
        fd_set rfds;
        int retval;
        pqueue_t *head;
        int block_usecs = -1; /* wait forever */
        /* watch terminal and socket for input */
        FD_ZERO(&rfds);
        FD_SET(gre_fd, &rfds);
        FD_SET(pty_fd, &rfds);
        /*
         * if there are multiple pending ACKs, then do a minimal timeout;
         * else if there is a single pending ACK then timeout after 0,5 seconds;
         * else block until data is available.
         */
        if (ack_sent != seq_recv) {
            if (ack_sent + 1 == seq_recv)  /* u_int wrap-around safe */
                block_usecs = 500000;
            else
                /* don't use zero, this will force a resceduling */
                /* when calling select(), giving pppd a chance to */
                /* run. */
                block_usecs = 1;
        }
        /* otherwise block_usecs == -1, which means wait forever */
        /*
         * If there is a packet in the queue, then don't wait longer than
         * the time remaining until it expires.
         */
        head = pqueue_head();
        if (head != NULL) {
            int expiry_time = pqueue_expiry_time(head);
            if (block_usecs == -1 || expiry_time < block_usecs)
                block_usecs = expiry_time;
        }
        if (block_usecs == -1) {
            tvp = NULL;
        } else {
            tvp = &tv;
            tv.tv_usec = block_usecs;
            tv.tv_sec  = tv.tv_usec / 1000000;
            tv.tv_usec %= 1000000;
        }
        retval = select(max_fd + 1, &rfds, NULL, NULL, tvp);
        if (retval > 0 && FD_ISSET(pty_fd, &rfds)) {
            if (decaps_hdlc(pty_fd, encaps_gre,  gre_fd) < 0)
                break;
        } else if (retval == 0 && ack_sent != seq_recv) {
            /* if outstanding ack */
            /* send ack with no payload */
            encaps_gre(gre_fd, NULL, 0);         
        }
        if (retval > 0 && FD_ISSET(gre_fd, &rfds)) {
            if (decaps_gre (gre_fd, encaps_hdlc, pty_fd) < 0)
                break;
        }
        if (dequeue_gre (encaps_hdlc, pty_fd) < 0)
            break;
    }
    /* Close up when done. */
    close(gre_fd);
    close(pty_fd);
}