|
|
|
|
|
|
|
/*
|
|
|
|
* kPPP: A pppd Front End for the KDE project
|
|
|
|
*
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (C) 1997,98 Bernd Johannes Wuebben,
|
|
|
|
* Mario Weilguni
|
|
|
|
* Copyright (C) 1998-2002 Harri Porten <porten@kde.org>
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Library 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
|
|
|
|
* Library General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
* License along with this program; if not, write to the Free
|
|
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* A note to developers:
|
|
|
|
*
|
|
|
|
* Apart from the first dozen lines in main() the following code represents
|
|
|
|
* the setuid root part of kppp. So please be careful !
|
|
|
|
* o restrain from using X, TQt or KDE library calls
|
|
|
|
* o check for possible buffer overflows
|
|
|
|
* o handle requests from the parent process with care. They might be forged.
|
|
|
|
* o be paranoid and think twice about everything you change.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#if defined(__osf__) || defined(__svr4__)
|
|
|
|
#define _POSIX_PII_SOCKET
|
|
|
|
extern "C" int sethostname(char *name, int name_len);
|
|
|
|
#if !defined(__osf__)
|
|
|
|
extern "C" int _Psendmsg(int, void*, int);
|
|
|
|
extern "C" int _Precvmsg(int, void*, int);
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "kpppconfig.h"
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/uio.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <sys/param.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
|
|
|
#ifdef __FreeBSD__
|
|
|
|
# include <sys/linker.h> // for kldload
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef HAVE_NET_IF_PPP_H
|
|
|
|
# if defined(__DragonFly__)
|
|
|
|
# include <net/ppp_layer/ppp_defs.h>
|
|
|
|
# include <net/if.h>
|
|
|
|
# include <net/ppp/if_ppp.h>
|
|
|
|
# elif defined HAVE_LINUX_IF_PPP_H
|
|
|
|
# ifndef aligned_u64
|
|
|
|
# define aligned_u64 unsigned long long __attribute__((aligned(8)))
|
|
|
|
# endif
|
|
|
|
# include <linux/if_ppp.h>
|
|
|
|
# endif
|
|
|
|
#else
|
|
|
|
# include <net/ppp_defs.h>
|
|
|
|
# include <net/if.h>
|
|
|
|
# include <net/if_ppp.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <regex.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <termios.h>
|
|
|
|
|
|
|
|
#include "opener.h"
|
|
|
|
#include "devices.h"
|
|
|
|
|
|
|
|
#ifdef HAVE_RESOLV_H
|
|
|
|
# include <arpa/nameser.h>
|
|
|
|
# include <resolv.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef _PATH_RESCONF
|
|
|
|
#define _PATH_RESCONF "/etc/resolv.conf"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef _XPG4_2
|
|
|
|
extern "C" {
|
|
|
|
ssize_t recvmsg(int, struct msghdr *, int);
|
|
|
|
ssize_t sendmsg(int, const struct msghdr *, int);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define MY_ASSERT(x) if (!(x)) { \
|
|
|
|
fprintf(stderr, "ASSERT: \"%s\" in %s (%d)\n",#x,__FILE__,__LINE__); \
|
|
|
|
exit(1); }
|
|
|
|
|
|
|
|
#define MY_DEBUG
|
|
|
|
#ifndef MY_DEBUG
|
|
|
|
#define Debug(s) ((void)0);
|
|
|
|
#define Debug2(s, i) ((void)0);
|
|
|
|
#else
|
|
|
|
#define Debug(s) fprintf(stderr, (s "\n"));
|
|
|
|
#define Debug2(s, i) fprintf(stderr, (s), (i));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void sighandler_child(int);
|
|
|
|
static pid_t pppdPid = -1;
|
|
|
|
static int pppdExitStatus = -1;
|
|
|
|
static int checkForInterface();
|
|
|
|
|
|
|
|
// processing will stop at first file that could be opened successfully
|
|
|
|
const char * const kppp_syslog[] = { "/var/log/syslog.ppp",
|
|
|
|
"/var/log/syslog",
|
|
|
|
"/var/log/messages",
|
|
|
|
0 };
|
|
|
|
|
|
|
|
Opener::Opener(int s) : socket(s), ttyfd(-1) {
|
|
|
|
lockfile[0] = '\0';
|
|
|
|
signal(SIGUSR1, SIG_IGN);
|
|
|
|
signal(SIGTERM, SIG_IGN);
|
|
|
|
signal(SIGINT, SIG_IGN);
|
|
|
|
signal(SIGCHLD, sighandler_child);
|
|
|
|
mainLoop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Opener::mainLoop() {
|
|
|
|
|
|
|
|
int len;
|
|
|
|
int fd = -1;
|
|
|
|
int flags, mode;
|
|
|
|
const char *device, * const *logFile;
|
|
|
|
union AllRequests request;
|
|
|
|
struct ResponseHeader response;
|
|
|
|
struct msghdr msg;
|
|
|
|
struct iovec iov;
|
|
|
|
|
|
|
|
iov.iov_base = IOV_BASE_CAST &request;
|
|
|
|
iov.iov_len = sizeof(request);
|
|
|
|
|
|
|
|
msg.msg_name = 0L;
|
|
|
|
msg.msg_namelen = 0;
|
|
|
|
msg.msg_iov = &iov;
|
|
|
|
msg.msg_iovlen = 1;
|
|
|
|
msg.msg_control = 0L;
|
|
|
|
msg.msg_controllen = 0;
|
|
|
|
|
|
|
|
// loop forever
|
|
|
|
while(1) {
|
|
|
|
len = recvmsg(socket, &msg, 0);
|
|
|
|
if(len < 0) {
|
|
|
|
switch(errno) {
|
|
|
|
case EINTR:
|
|
|
|
Debug("Opener: interrupted system call, continuing");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
perror("Opener: error reading from socket");
|
|
|
|
_exit(1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch(request.header.type) {
|
|
|
|
|
|
|
|
case OpenDevice:
|
|
|
|
Debug("Opener: received OpenDevice");
|
|
|
|
MY_ASSERT(len == sizeof(struct OpenModemRequest));
|
|
|
|
close(ttyfd);
|
|
|
|
device = deviceByIndex(request.modem.deviceNum);
|
|
|
|
response.status = 0;
|
|
|
|
if ((ttyfd = open(device, O_RDWR|O_NDELAY|O_NOCTTY)) == -1) {
|
|
|
|
Debug("error opening modem device !");
|
|
|
|
fd = open(DEVNULL, O_RDONLY);
|
|
|
|
response.status = -errno;
|
|
|
|
sendFD(fd, &response);
|
|
|
|
close(fd);
|
|
|
|
} else
|
|
|
|
sendFD(ttyfd, &response);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OpenLock:
|
|
|
|
Debug("Opener: received OpenLock\n");
|
|
|
|
MY_ASSERT(len == sizeof(struct OpenLockRequest));
|
|
|
|
flags = request.lock.flags;
|
|
|
|
MY_ASSERT(flags == O_RDONLY || flags == O_WRONLY|O_TRUNC|O_CREAT);
|
|
|
|
if(flags == O_WRONLY|O_TRUNC|O_CREAT)
|
|
|
|
mode = 0644;
|
|
|
|
else
|
|
|
|
mode = 0;
|
|
|
|
|
|
|
|
device = deviceByIndex(request.lock.deviceNum);
|
|
|
|
MY_ASSERT(strlen(LOCK_DIR)+strlen(device) < MaxPathLen);
|
|
|
|
strlcpy(lockfile, LOCK_DIR"/LCK..", MaxPathLen);
|
|
|
|
strlcat(lockfile, strrchr(device, '/') + 1, MaxPathLen );
|
|
|
|
response.status = 0;
|
|
|
|
// TODO:
|
|
|
|
// struct stat st;
|
|
|
|
// if(stat(lockfile.data(), &st) == -1) {
|
|
|
|
// if(errno == EBADF)
|
|
|
|
// return -1;
|
|
|
|
// } else {
|
|
|
|
// // make sure that this is a regular file
|
|
|
|
// if(!S_ISREG(st.st_mode))
|
|
|
|
// return -1;
|
|
|
|
// }
|
|
|
|
if ((fd = open(lockfile, flags, mode)) == -1) {
|
|
|
|
Debug("error opening lockfile!");
|
|
|
|
lockfile[0] = '\0';
|
|
|
|
fd = open(DEVNULL, O_RDONLY);
|
|
|
|
response.status = -errno;
|
|
|
|
} else
|
|
|
|
fchown(fd, 0, 0);
|
|
|
|
sendFD(fd, &response);
|
|
|
|
close(fd);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RemoveLock:
|
|
|
|
Debug("Opener: received RemoveLock");
|
|
|
|
MY_ASSERT(len == sizeof(struct RemoveLockRequest));
|
|
|
|
close(ttyfd);
|
|
|
|
ttyfd = -1;
|
|
|
|
response.status = unlink(lockfile);
|
|
|
|
lockfile[0] = '\0';
|
|
|
|
sendResponse(&response);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OpenResolv:
|
|
|
|
Debug("Opener: received OpenResolv");
|
|
|
|
MY_ASSERT(len == sizeof(struct OpenResolvRequest));
|
|
|
|
flags = request.resolv.flags;
|
|
|
|
response.status = 0;
|
|
|
|
if ((fd = open(_PATH_RESCONF, flags)) == -1) {
|
|
|
|
Debug("error opening resolv.conf!");
|
|
|
|
fd = open(DEVNULL, O_RDONLY);
|
|
|
|
response.status = -errno;
|
|
|
|
}
|
|
|
|
sendFD(fd, &response);
|
|
|
|
close(fd);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OpenSysLog:
|
|
|
|
Debug("Opener: received OpenSysLog");
|
|
|
|
MY_ASSERT(len == sizeof(struct OpenLogRequest));
|
|
|
|
response.status = 0;
|
|
|
|
logFile = &kppp_syslog[0];
|
|
|
|
while (*logFile) {
|
|
|
|
if ((fd = open(*logFile, O_RDONLY)) >= 0)
|
|
|
|
break;
|
|
|
|
logFile++;
|
|
|
|
}
|
|
|
|
if (!*logFile) {
|
|
|
|
Debug("No success opening a syslog file !");
|
|
|
|
fd = open(DEVNULL, O_RDONLY);
|
|
|
|
response.status = -errno;
|
|
|
|
}
|
|
|
|
sendFD(fd, &response);
|
|
|
|
close(fd);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SetSecret:
|
|
|
|
Debug("Opener: received SetSecret");
|
|
|
|
MY_ASSERT(len == sizeof(struct SetSecretRequest));
|
|
|
|
response.status = !createAuthFile(request.secret.method,
|
|
|
|
request.secret.username,
|
|
|
|
request.secret.password);
|
|
|
|
sendResponse(&response);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RemoveSecret:
|
|
|
|
Debug("Opener: received RemoveSecret");
|
|
|
|
MY_ASSERT(len == sizeof(struct RemoveSecretRequest));
|
|
|
|
response.status = !removeAuthFile(request.remove.method);
|
|
|
|
sendResponse(&response);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SetHostname:
|
|
|
|
Debug("Opener: received SetHostname");
|
|
|
|
MY_ASSERT(len == sizeof(struct SetHostnameRequest));
|
|
|
|
response.status = 0;
|
|
|
|
if(sethostname(request.host.name, strlen(request.host.name)))
|
|
|
|
response.status = -errno;
|
|
|
|
sendResponse(&response);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ExecPPPDaemon:
|
|
|
|
Debug("Opener: received ExecPPPDaemon");
|
|
|
|
MY_ASSERT(len == sizeof(struct ExecDaemonRequest));
|
|
|
|
response.status = execpppd(request.daemon.arguments);
|
|
|
|
sendResponse(&response);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KillPPPDaemon:
|
|
|
|
Debug("Opener: received KillPPPDaemon");
|
|
|
|
MY_ASSERT(len == sizeof(struct KillDaemonRequest));
|
|
|
|
response.status = killpppd();
|
|
|
|
sendResponse(&response);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PPPDExitStatus:
|
|
|
|
Debug("Opener: received PPPDExitStatus");
|
|
|
|
MY_ASSERT(len == sizeof(struct PPPDExitStatusRequest));
|
|
|
|
response.status = pppdExitStatus;
|
|
|
|
sendResponse(&response);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Stop:
|
|
|
|
Debug("Opener: received STOP command");
|
|
|
|
_exit(0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Debug("Opener: unknown command type. Exiting ...");
|
|
|
|
_exit(1);
|
|
|
|
}
|
|
|
|
} // else
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Send an open fd over a UNIX socket pair
|
|
|
|
//
|
|
|
|
int Opener::sendFD(int fd, struct ResponseHeader *response) {
|
|
|
|
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
char _cmsg[sizeof(struct cmsghdr)];
|
|
|
|
char data[sizeof(int)]; // int fd field
|
|
|
|
};
|
|
|
|
struct cmsghdr cmsg;
|
|
|
|
} control;
|
|
|
|
struct msghdr msg;
|
|
|
|
struct iovec iov;
|
|
|
|
|
|
|
|
msg.msg_name = 0L;
|
|
|
|
msg.msg_namelen = 0;
|
|
|
|
msg.msg_iov = &iov;
|
|
|
|
msg.msg_iovlen = 1;
|
|
|
|
|
|
|
|
// Send data
|
|
|
|
iov.iov_base = IOV_BASE_CAST response;
|
|
|
|
iov.iov_len = sizeof(struct ResponseHeader);
|
|
|
|
|
|
|
|
// Send a (duplicate of) the file descriptor
|
|
|
|
control.cmsg.cmsg_len = sizeof(struct cmsghdr) + sizeof(int);
|
|
|
|
control.cmsg.cmsg_level = SOL_SOCKET;
|
|
|
|
control.cmsg.cmsg_type = MY_SCM_RIGHTS;
|
|
|
|
|
|
|
|
msg.msg_control = (char *) &control;
|
|
|
|
msg.msg_controllen = control.cmsg.cmsg_len;
|
|
|
|
|
|
|
|
#ifdef CMSG_DATA
|
|
|
|
*((int *)CMSG_DATA(&control.cmsg)) = fd;
|
|
|
|
#else
|
|
|
|
*((int *) &control.cmsg.cmsg_data) = fd;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (sendmsg(socket, &msg, 0) < 0) {
|
|
|
|
perror("unable to send file descriptors");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Opener::sendResponse(struct ResponseHeader *response) {
|
|
|
|
|
|
|
|
struct msghdr msg;
|
|
|
|
struct iovec iov;
|
|
|
|
|
|
|
|
msg.msg_name = 0L;
|
|
|
|
msg.msg_namelen = 0;
|
|
|
|
msg.msg_iov = &iov;
|
|
|
|
msg.msg_iovlen = 1;
|
|
|
|
msg.msg_control = 0L;
|
|
|
|
msg.msg_controllen = 0;
|
|
|
|
|
|
|
|
// Send data
|
|
|
|
iov.iov_base = IOV_BASE_CAST response;
|
|
|
|
iov.iov_len = sizeof(struct ResponseHeader);
|
|
|
|
|
|
|
|
if (sendmsg(socket, &msg, 0) < 0) {
|
|
|
|
perror("unable to send response");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* Opener::deviceByIndex(int idx) {
|
|
|
|
|
|
|
|
const char *device = 0L;
|
|
|
|
|
|
|
|
for(int i = 0; devices[i]; i++)
|
|
|
|
if(i == idx)
|
|
|
|
device = devices[i];
|
|
|
|
MY_ASSERT(device);
|
|
|
|
return device;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Opener::createAuthFile(Auth method, char *username, char *password) {
|
|
|
|
const char *authfile, *oldName, *newName;
|
|
|
|
char line[100];
|
|
|
|
char regexp[2*MaxStrLen+30];
|
|
|
|
regex_t preg;
|
|
|
|
|
|
|
|
if(!(authfile = authFile(method)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if(!(newName = authFile(method, New)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// look for username, "username" or 'username'
|
|
|
|
// if you modify this RE you have to adapt regexp's size above
|
|
|
|
snprintf(regexp, sizeof(regexp), "^[ \t]*%s[ \t]\\|^[ \t]*[\"\']%s[\"\']",
|
|
|
|
username,username);
|
|
|
|
MY_ASSERT(regcomp(&preg, regexp, 0) == 0);
|
|
|
|
|
|
|
|
// copy to new file pap- or chap-secrets
|
|
|
|
int old_umask = umask(0077);
|
|
|
|
FILE *fout = fopen(newName, "w");
|
|
|
|
if(fout) {
|
|
|
|
// copy old file
|
|
|
|
FILE *fin = fopen(authfile, "r");
|
|
|
|
if(fin) {
|
|
|
|
while(fgets(line, sizeof(line), fin)) {
|
|
|
|
if(regexec(&preg, line, 0, 0L, 0) == 0)
|
|
|
|
continue;
|
|
|
|
fputs(line, fout);
|
|
|
|
}
|
|
|
|
fclose(fin);
|
|
|
|
}
|
|
|
|
|
|
|
|
// append user/pass pair
|
|
|
|
fprintf(fout, "\"%s\"\t*\t\"%s\"\n", username, password);
|
|
|
|
fclose(fout);
|
|
|
|
}
|
|
|
|
|
|
|
|
// restore umask
|
|
|
|
umask(old_umask);
|
|
|
|
|
|
|
|
// free memory allocated by regcomp
|
|
|
|
regfree(&preg);
|
|
|
|
|
|
|
|
if(!(oldName = authFile(method, Old)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// delete old file if any
|
|
|
|
unlink(oldName);
|
|
|
|
|
|
|
|
rename(authfile, oldName);
|
|
|
|
rename(newName, authfile);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Opener::removeAuthFile(Auth method) {
|
|
|
|
const char *authfile, *oldName;
|
|
|
|
|
|
|
|
if(!(authfile = authFile(method)))
|
|
|
|
return false;
|
|
|
|
if(!(oldName = authFile(method, Old)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if(access(oldName, F_OK) == 0) {
|
|
|
|
unlink(authfile);
|
|
|
|
return (rename(oldName, authfile) == 0);
|
|
|
|
} else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char* Opener::authFile(Auth method, int version) {
|
|
|
|
switch(method|version) {
|
|
|
|
case PAP|Original:
|
|
|
|
return PAP_AUTH_FILE;
|
|
|
|
break;
|
|
|
|
case PAP|New:
|
|
|
|
return PAP_AUTH_FILE".new";
|
|
|
|
break;
|
|
|
|
case PAP|Old:
|
|
|
|
return PAP_AUTH_FILE".old";
|
|
|
|
break;
|
|
|
|
case CHAP|Original:
|
|
|
|
return CHAP_AUTH_FILE;
|
|
|
|
break;
|
|
|
|
case CHAP|New:
|
|
|
|
return CHAP_AUTH_FILE".new";
|
|
|
|
break;
|
|
|
|
case CHAP|Old:
|
|
|
|
return CHAP_AUTH_FILE".old";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0L;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Opener::execpppd(const char *arguments) {
|
|
|
|
char buf[MAX_CMDLEN];
|
|
|
|
char *args[MaxArgs];
|
|
|
|
pid_t pgrpid;
|
|
|
|
|
|
|
|
if(ttyfd<0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
pppdExitStatus = -1;
|
|
|
|
|
|
|
|
switch(pppdPid = fork())
|
|
|
|
{
|
|
|
|
case -1:
|
|
|
|
fprintf(stderr,"In parent: fork() failed\n");
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
// let's parse the arguments the user supplied into UNIX suitable form
|
|
|
|
// that is a list of pointers each pointing to exactly one word
|
|
|
|
strlcpy(buf, arguments, sizeof(buf));
|
|
|
|
parseargs(buf, args);
|
|
|
|
// become a session leader and let /dev/ttySx
|
|
|
|
// be the controlling terminal.
|
|
|
|
pgrpid = setsid();
|
|
|
|
#ifdef TIOCSCTTY
|
|
|
|
if(ioctl(ttyfd, TIOCSCTTY, 0)<0)
|
|
|
|
fprintf(stderr, "ioctl() failed.\n");
|
|
|
|
#elif defined (TIOCSPGRP)
|
|
|
|
if(ioctl(ttyfd, TIOCSPGRP, &pgrpid)<0)
|
|
|
|
fprintf(stderr, "ioctl() failed.\n");
|
|
|
|
#endif
|
|
|
|
if(tcsetpgrp(ttyfd, pgrpid)<0)
|
|
|
|
fprintf(stderr, "tcsetpgrp() failed.\n");
|
|
|
|
|
|
|
|
dup2(ttyfd, 0);
|
|
|
|
dup2(ttyfd, 1);
|
|
|
|
|
|
|
|
switch (checkForInterface()) {
|
|
|
|
case 1:
|
|
|
|
fprintf(stderr, "Cannot determine if kernel supports ppp.\n");
|
|
|
|
break;
|
|
|
|
case -1:
|
|
|
|
fprintf(stderr, "Kernel does not support ppp, oops.\n");
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
fprintf(stderr, "Kernel supports ppp alright.\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
execve(pppdPath(), args, 0L);
|
|
|
|
_exit(0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Debug2("In parent: pppd pid %d\n",pppdPid);
|
|
|
|
close(ttyfd);
|
|
|
|
ttyfd = -1;
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Opener::killpppd()const {
|
|
|
|
if(pppdPid > 0) {
|
|
|
|
Debug2("In killpppd(): Sending SIGTERM to %d\n", pppdPid);
|
|
|
|
if(kill(pppdPid, SIGTERM) < 0) {
|
|
|
|
Debug2("Error terminating %d. Sending SIGKILL\n", pppdPid);
|
|
|
|
if(kill(pppdPid, SIGKILL) < 0) {
|
|
|
|
Debug2("Error killing %d\n", pppdPid);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Opener::parseargs(char* buf, char** args) {
|
|
|
|
int nargs = 0;
|
|
|
|
int quotes;
|
|
|
|
|
|
|
|
while(nargs < MaxArgs-1 && *buf != '\0') {
|
|
|
|
|
|
|
|
quotes = 0;
|
|
|
|
|
|
|
|
// Strip whitespace. Use nulls, so that the previous argument is
|
|
|
|
// terminated automatically.
|
|
|
|
|
|
|
|
while ((*buf == ' ' ) || (*buf == '\t' ) || (*buf == '\n' ) )
|
|
|
|
*buf++ = '\0';
|
|
|
|
|
|
|
|
// detect begin of quoted argument
|
|
|
|
if (*buf == '"' || *buf == '\'') {
|
|
|
|
quotes = *buf;
|
|
|
|
*buf++ = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
// save the argument
|
|
|
|
if(*buf != '\0') {
|
|
|
|
*args++ = buf;
|
|
|
|
nargs++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!quotes)
|
|
|
|
while ((*buf != '\0') && (*buf != '\n') &&
|
|
|
|
(*buf != '\t') && (*buf != ' '))
|
|
|
|
buf++;
|
|
|
|
else {
|
|
|
|
while ((*buf != '\0') && (*buf != quotes))
|
|
|
|
buf++;
|
|
|
|
*buf++ = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*args = 0L;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char* pppdPath() {
|
|
|
|
// wasting a few bytes
|
|
|
|
static char buffer[sizeof(PPPDSEARCHPATH)+sizeof(PPPDNAME)];
|
|
|
|
static char *pppdPath = 0L;
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
if(pppdPath == 0L) {
|
|
|
|
const char *c = PPPDSEARCHPATH;
|
|
|
|
while(*c != '\0') {
|
|
|
|
while(*c == ':')
|
|
|
|
c++;
|
|
|
|
p = buffer;
|
|
|
|
while(*c != '\0' && *c != ':')
|
|
|
|
*p++ = *c++;
|
|
|
|
*p = '\0';
|
|
|
|
strcat(p, "/");
|
|
|
|
strcat(p, PPPDNAME);
|
|
|
|
if(access(buffer, F_OK) == 0)
|
|
|
|
return (pppdPath = buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return pppdPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
int checkForInterface()
|
|
|
|
{
|
|
|
|
// I don't know if Linux needs more initialization to get the ioctl to
|
|
|
|
// work, pppd seems to hint it does. But BSD doesn't, and the following
|
|
|
|
// code should compile.
|
|
|
|
#if (defined(HAVE_NET_IF_PPP_H) || defined(HAVE_LINUX_IF_PPP_H)) && !defined(__svr4__)
|
|
|
|
int s, ok;
|
|
|
|
struct ifreq ifr;
|
|
|
|
// extern char *no_ppp_msg;
|
|
|
|
|
|
|
|
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
|
|
|
|
return 1; /* can't tell */
|
|
|
|
|
|
|
|
strlcpy(ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
|
|
|
|
ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
|
|
|
|
close(s);
|
|
|
|
|
|
|
|
if (ok == -1) {
|
|
|
|
// This is ifdef'd FreeBSD, because FreeBSD is the only BSD that supports
|
|
|
|
// KLDs, the old LKM interface couldn't handle loading devices
|
|
|
|
// dynamically, and thus can't load ppp support on the fly
|
|
|
|
#ifdef __FreeBSD__
|
|
|
|
// If we failed to load ppp support and don't have it already.
|
|
|
|
if (kldload("if_ppp") == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
// We attempt to use the SunOS/SysVr4 method and stat /dev/ppp
|
|
|
|
struct stat buf;
|
|
|
|
|
|
|
|
memset(&buf, 0, sizeof(buf));
|
|
|
|
return stat("/dev/ppp", &buf);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void sighandler_child(int) {
|
|
|
|
pid_t pid;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
signal(SIGCHLD, sighandler_child);
|
|
|
|
if(pppdPid>0) {
|
|
|
|
pid = waitpid(pppdPid, &status, WNOHANG);
|
|
|
|
if(pid != pppdPid) {
|
|
|
|
fprintf(stderr, "received SIGCHLD from unknown origin.\n");
|
|
|
|
} else {
|
|
|
|
Debug("It was pppd that died");
|
|
|
|
pppdPid = -1;
|
|
|
|
if((WIFEXITED(status))) {
|
|
|
|
pppdExitStatus = (WEXITSTATUS(status));
|
|
|
|
Debug2("pppd exited with return value %d\n", pppdExitStatus);
|
|
|
|
} else {
|
|
|
|
pppdExitStatus = 99;
|
|
|
|
Debug("pppd exited abnormally.");
|
|
|
|
}
|
|
|
|
Debug2("Sending %i a SIGUSR1\n", getppid());
|
|
|
|
kill(getppid(), SIGUSR1);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
fprintf(stderr, "received unexpected SIGCHLD.\n");
|
|
|
|
}
|