/*
* remconn.CXX
*
* Remote network connection (ppp) class implementation
*
* Portable Windows Library
*
* Copyright (c) 1993-1998 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): ______________________________________.
*
* $Log: remconn.cxx,v $
* Revision 1.20 2004/02/22 03:36:41 ykiryanov
* Added inclusion of signal.h on BeOS to define SIGINT
*
* Revision 1.19 2003/12/02 10:46:15 csoutheren
* Added patch for Solaris, thanks to Michal Zygmuntowicz
*
* Revision 1.18 2002/10/10 04:43:44 robertj
* VxWorks port, thanks Martijn Roest
*
* Revision 1.17 1998/11/30 21:51:49 robertj
* New directory structure.
*
* Revision 1.16 1998/09/24 04:12:15 robertj
* Added open software license.
*
*/
#pragma implementation "remconn.h"
#include <ptlib.h>
#include <ptlib/pipechan.h>
#include <ptlib/remconn.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#ifdef P_VXWORKS
#include <socklib.h>
#endif // P_VXWORKS
#ifdef P_SOLARIS
#include <signal.h>
#endif
#ifdef __BEOS__
#include <signal.h>
#endif
#include "uerror.h"
static const PString RasStr = "ras";
static const PString NumberStr = "Number";
static const PCaselessString UsernameStr = "$USERID";
static const PCaselessString PasswordStr = "$PASSWORD";
static const PString AddressStr = "Address";
static const PString NameServerStr = "NameServer";
static const PString OptionsStr = "Options";
static const PString DeviceStr = "Device";
static const PString DefaultDevice = "ppp0";
static const PString PPPDStr = "PPPD";
static const PString DefaultPPPD = "pppd";
static const PString ChatStr = "Chat";
static const PString DefaultChat = "chat";
static const PString PortStr = "Port";
static const PString DefaultPort = "/dev/modem";
static const PString DialPrefixStr = "DialPrefix";
static const PString DefaultDialPrefix = "ATDT";
static const PString LoginStr = "Login";
static const PString DefaultLogin = "'' sername: $USERID assword: $PASSWORD";
static const PString TimeoutStr = "TimeoutStr";
static const PString DefaultTimeout = "90";
static const PString PPPDOptsStr = "PPPDOpts";
static const PString PPPDOpts = "-detach";
static const PString DefaultPPPDOpts = "crtscts modem defaultroute lock";
static const PString BaudRateStr = "BaudRate";
static const PString DefaultBaudRate = "57600";
static const PString ErrorsStr = "Errors";
static const PString DefaultErrors = "ABORT 'NO CARRIER' ABORT BUSY ABORT 'NO DIALTONE'";
static const PString InitStr = "Init";
static const PString DefaultInit = "'' ATE1Q0Z OK";
static const PXErrorStruct ErrorTable[] =
{
// Remote connection errors
{ 1000, "Attempt to open remote connection with empty system name" },
{ 1001, "Attempt to open connection to unknown remote system"},
{ 1002, "pppd could not connect to remote system"},
};
static int PPPDeviceStatus(const char * devName);
PRemoteConnection::PRemoteConnection()
{
Construct();
}
PRemoteConnection::PRemoteConnection(const PString & name)
: remoteName(name)
{
Construct();
}
PRemoteConnection::~PRemoteConnection()
{
Close();
}
BOOL PRemoteConnection::Open(const PString & name, BOOL existing)
{
return Open(name, "", "", existing);
}
BOOL PRemoteConnection::Open(const PString & name,
const PString & user,
const PString & pword,
BOOL existing)
{
userName = user;
password = pword;
// cannot open remote connection with an empty name
if (name.IsEmpty()) {
status = NoNameOrNumber;
PProcess::PXShowSystemWarning(1000, ErrorTable[0].str);
return FALSE;
}
// cannot open remote connection not in config file
PConfig config(0, RasStr);
PString phoneNumber;
if ((phoneNumber = config.GetString(name, NumberStr, "")).IsEmpty()) {
status = NoNameOrNumber;
PProcess::PXShowSystemWarning(1001, ErrorTable[1].str);
return FALSE;
}
// if there is a connection active, check to see if it has the same name
if (pipeChannel != NULL &&
pipeChannel->IsRunning() &&
name == remoteName &&
PPPDeviceStatus(deviceStr) > 0) {
osError = errno;
status = Connected;
return TRUE;
}
osError = errno;
if (existing)
return FALSE;
Close();
// name = name of configuration
// sectionName = name of config section
remoteName = name;
///////////////////////////////////////////
//
// get global options
//
config.SetDefaultSection(OptionsStr);
deviceStr = config.GetString(DeviceStr, DefaultDevice);
PString pppdStr = config.GetString(PPPDStr, DefaultPPPD);
PString chatStr = config.GetString(ChatStr, DefaultChat);
PString baudRate = config.GetString(BaudRateStr, DefaultBaudRate);
PString chatErrs = config.GetString(ErrorsStr, DefaultErrors);
PString modemInit = config.GetString(InitStr, DefaultInit);
PString dialPrefix = config.GetString(DialPrefixStr, DefaultDialPrefix);
PString pppdOpts = config.GetString(PPPDOptsStr, DefaultPPPDOpts);
///////////////////////////////////////////
//
// get remote system parameters
//
config.SetDefaultSection(remoteName);
PString portName = config.GetString(PortStr,
config.GetString(OptionsStr, PortStr, DefaultPort));
PString loginStr = config.GetString(LoginStr, DefaultLogin);
PString timeoutStr = config.GetString(TimeoutStr, DefaultTimeout);
PINDEX timeout = timeoutStr.AsInteger();
PString addressStr = config.GetString(AddressStr, "");
PString nameServerStr = config.GetString(NameServerStr, "");
///////////////////////////////////////////
//
// start constructing the command argument array
//
PStringArray argArray;
PINDEX argCount = 0;
argArray[argCount++] = portName;
argArray[argCount++] = baudRate;
PStringArray tokens = PPPDOpts.Tokenise(' ');
PINDEX i;
for (i = 0; i < tokens.GetSize(); i++)
argArray[argCount++] = tokens[i];
tokens = pppdOpts.Tokenise(' ');
for (i = 0; i < tokens.GetSize(); i++)
argArray[argCount++] = tokens[i];
if (!nameServerStr.IsEmpty()) {
argArray[argCount++] = "ipparam";
argArray[argCount++] = nameServerStr;
}
///////////////////////////////////////////
//
// replace metastrings in the login string
//
loginStr.Replace(UsernameStr, user);
loginStr.Replace(PasswordStr, pword);
///////////////////////////////////////////
//
// setup the chat command
//
PString chatCmd = chatErrs & modemInit & dialPrefix + phoneNumber & loginStr;
if (!chatCmd.IsEmpty()) {
argArray[argCount++] = "connect";
argArray[argCount++] = chatStr & "-t" + timeoutStr & chatCmd;
}
if (!addressStr)
argArray[argCount++] = addressStr + ":";
///////////////////////////////////////////
//
// instigate a dial using pppd
//
pipeChannel = PNEW PPipeChannel(pppdStr, argArray);
osError = errno;
///////////////////////////////////////////
//
// wait until the dial succeeds, or times out
//
PTimer timer(timeout*1000);
for (;;) {
if (pipeChannel == NULL || !pipeChannel->IsRunning())
break;
if (PPPDeviceStatus(deviceStr) > 0) {
osError = errno;
return TRUE;
}
if (!timer.IsRunning())
break;
PThread::Current()->Sleep(1000);
}
osError = errno;
///////////////////////////////////////////
//
// dial failed
//
Close();
return FALSE;
}
PObject::Comparison PRemoteConnection::Compare(const PObject & obj) const
{
return remoteName.Compare(((const PRemoteConnection &)obj).remoteName);
}
PINDEX PRemoteConnection::HashFunction() const
{
return remoteName.HashFunction();
}
void PRemoteConnection::Construct()
{
pipeChannel = NULL;
status = Idle;
osError = 0;
}
BOOL PRemoteConnection::Open(BOOL existing)
{
return Open(remoteName, existing);
}
void PRemoteConnection::Close()
{
if (pipeChannel != NULL) {
// give pppd a chance to clean up
pipeChannel->Kill(SIGINT);
PTimer timer(10*1000);
for (;;) {
if (!pipeChannel->IsRunning() ||
(PPPDeviceStatus(deviceStr) <= 0) ||
!timer.IsRunning())
break;
PThread::Current()->Sleep(1000);
}
// kill the connection for real
delete pipeChannel;
pipeChannel = NULL;
}
}
PRemoteConnection::Status PRemoteConnection::GetStatus() const
{
if (pipeChannel != NULL &&
pipeChannel->IsRunning() &&
PPPDeviceStatus(deviceStr) > 0)
return Connected;
return Idle;
}
PStringArray PRemoteConnection::GetAvailableNames()
{
PStringArray names;
// get the list of remote system names from the system config file
PConfig config(0, RasStr);
// remotes have section names of the form "Remote-x" where X is some
// unique identifier, usually a number or the system name
PStringList sections = config.GetSections();
for (PINDEX i = 0; i < sections.GetSize(); i++) {
PString sectionName = sections[i];
if (sectionName != OptionsStr)
names[names.GetSize()] = sectionName;
}
return names;
}
//
// <0 = does not exist
// 0 = exists, but is down
// >0 = exists, is up
//
static int PPPDeviceStatus(const char * devName)
{
#if defined(HAS_IFREQ)
int skfd;
struct ifreq ifr;
// Create a channel to the NET kernel.
if ((skfd = socket(AF_INET, SOCK_DGRAM,0)) < 0)
return -1;
// attempt to get the status of the ppp connection
int stat;
strcpy(ifr.ifr_name, devName);
if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0)
stat = -1;
else
stat = (ifr.ifr_flags & IFF_UP) ? 1 : 0;
// attempt to get the status of the ppp connection
close(skfd);
return stat;
#else
#warning "No PPPDeviceExists implementation defined"
return FALSE;
#endif
}
PRemoteConnection::Status PRemoteConnection::GetConfiguration(
Configuration & config // Configuration of remote connection
)
{
return GetConfiguration(remoteName, config);
}
PRemoteConnection::Status PRemoteConnection::GetConfiguration(
const PString & name, // Remote connection name to get configuration
Configuration & config // Configuration of remote connection
)
{
if (name.IsEmpty())
return NoNameOrNumber;
PConfig cfg(0, RasStr);
if (cfg.GetString(name, NumberStr, "").IsEmpty())
return NoNameOrNumber;
cfg.SetDefaultSection(name);
config.device = cfg.GetString(OptionsStr, PortStr, DefaultPort);
config.phoneNumber = cfg.GetString(NumberStr);
config.ipAddress = cfg.GetString(AddressStr);
config.dnsAddress = cfg.GetString(NameServerStr);
config.script = cfg.GetString(LoginStr, DefaultLogin);
config.subEntries = 0;
config.dialAllSubEntries = FALSE;
return Connected;
}
PRemoteConnection::Status PRemoteConnection::SetConfiguration(
const Configuration & config, // Configuration of remote connection
BOOL create // Flag to create connection if not present
)
{
return SetConfiguration(remoteName, config, create);
}
PRemoteConnection::Status PRemoteConnection::SetConfiguration(
const PString & name, // Remote connection name to configure
const Configuration & config, // Configuration of remote connection
BOOL create // Flag to create connection if not present
)
{
if (config.phoneNumber.IsEmpty())
return GeneralFailure;
PConfig cfg(0, RasStr);
if (!create && cfg.GetString(name, NumberStr, "").IsEmpty())
return NoNameOrNumber;
cfg.SetDefaultSection(name);
if (config.device.IsEmpty())
cfg.DeleteKey(PortStr);
else
cfg.SetString(PortStr, config.device);
cfg.SetString(NumberStr, config.phoneNumber);
if (config.ipAddress.IsEmpty())
cfg.DeleteKey(AddressStr);
else
cfg.SetString(AddressStr, config.ipAddress);
if (config.dnsAddress.IsEmpty())
cfg.DeleteKey(NameServerStr);
else
cfg.SetString(NameServerStr, config.dnsAddress);
if (config.script.IsEmpty())
cfg.DeleteKey(LoginStr);
else
cfg.SetString(LoginStr, config.script);
return Connected;
}
PRemoteConnection::Status PRemoteConnection::RemoveConfiguration(
const PString & name // Remote connection to remove
)
{
PConfig cfg(0, RasStr);
if (cfg.GetString(name, NumberStr, "").IsEmpty())
return NoNameOrNumber;
cfg.DeleteSection(name);
return Connected;
}
// End of File ////////////////////////////////////////////////////////////////
syntax highlighted by Code2HTML, v. 0.9.1