1 // -*- c++ -*- //////////////////////////////////////////////////////////////// 
   2 // Name:        unix/dialup.cpp 
   3 // Purpose:     Network related wxWindows classes and functions 
   4 // Author:      Karsten Ballüder 
   8 // Copyright:   (c) Karsten Ballüder 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  15 #   pragma implementation "dialup.h" 
  18 #if wxUSE_DIALUP_MANAGER 
  24 #include "wx/string.h" 
  26 #include "wx/dialup.h" 
  28 #include "wx/filefn.h" 
  32 #include "wx/process.h" 
  35 #include "wx/wxchar.h" 
  42 #define __STRICT_ANSI__ 
  43 #include <sys/socket.h> 
  45 #include <netinet/in.h> 
  46 #include <arpa/inet.h> 
  49 DEFINE_EVENT_TYPE(wxEVT_DIALUP_CONNECTED
) 
  50 DEFINE_EVENT_TYPE(wxEVT_DIALUP_DISCONNECTED
) 
  52 // ---------------------------------------------------------------------------- 
  53 // A class which groups functions dealing with connecting to the network from a 
  54 // workstation using dial-up access to the net. There is at most one instance 
  55 // of this class in the program accessed via GetDialUpManager(). 
  56 // ---------------------------------------------------------------------------- 
  60  * 1. more configurability for Unix: i.e. how to initiate the connection, how 
  61  *    to check for online status, &c. 
  62  * 2. add a "long Dial(long connectionId = -1)" function which asks the user 
  63  *    about which connection to dial (this may be done using native dialogs 
  64  *    under NT, need generic dialogs for all others) and returns the identifier 
  65  *    of the selected connection (it's opaque to the application) - it may be 
  66  *    reused later to dial the same connection later (or use strings instead of 
  68  * 3. add an async version of dialing functions which notify the caller about 
  69  *    the progress (or may be even start another thread to monitor it) 
  70  * 4. the static creation/accessor functions are not MT-safe - but is this 
  71  *    really crucial? I think we may suppose they're always called from the 
  75 class WXDLLEXPORT wxDialUpManagerImpl 
: public wxDialUpManager
 
  78    wxDialUpManagerImpl(); 
  79    ~wxDialUpManagerImpl(); 
  81    /** Could the dialup manager be initialized correctly? If this function 
  82        returns FALSE, no other functions will work neither, so it's a good idea 
  83        to call this function and check its result before calling any other 
  84        wxDialUpManager methods. 
  86    virtual bool IsOk() const 
  89    /** The simplest way to initiate a dial up: this function dials the given 
  90        ISP (exact meaning of the parameter depends on the platform), returns 
  91        TRUE on success or FALSE on failure and logs the appropriate error 
  92        message in the latter case. 
  93        @param nameOfISP optional paramater for dial program 
  94        @param username unused 
  95        @param password unused 
  97    virtual bool Dial(const wxString
& nameOfISP
, 
  98                      const wxString
& WXUNUSED(username
), 
  99                      const wxString
& WXUNUSED(password
), 
 102    // Hang up the currently active dial up connection. 
 103    virtual bool HangUp(); 
 105    // returns TRUE if the computer is connected to the network: under Windows, 
 106    // this just means that a RAS connection exists, under Unix we check that 
 107    // the "well-known host" (as specified by SetWellKnownHost) is reachable 
 108    virtual bool IsOnline() const 
 111          return m_IsOnline 
== Net_Connected
; 
 114    // do we have a constant net connection? 
 115    virtual bool IsAlwaysOnline() const; 
 117    // returns TRUE if (async) dialing is in progress 
 118    virtual bool IsDialing() const 
 119       { return m_DialProcess 
!= NULL
; } 
 121    // cancel dialing the number initiated with Dial(async = TRUE) 
 122    // NB: this won't result in DISCONNECTED event being sent 
 123    virtual bool CancelDialing(); 
 125    size_t GetISPNames(class wxArrayString 
&) const 
 128    // sometimes the built-in logic for determining the online status may fail, 
 129    // so, in general, the user should be allowed to override it. This function 
 130    // allows to forcefully set the online status - whatever our internal 
 131    // algorithm may think about it. 
 132    virtual void SetOnlineStatus(bool isOnline 
= TRUE
) 
 133       { m_IsOnline 
= isOnline 
? Net_Connected 
: Net_No
; } 
 135    // set misc wxDialUpManager options 
 136    // -------------------------------- 
 138    // enable automatical checks for the connection status and sending of 
 139    // wxEVT_DIALUP_CONNECTED/wxEVT_DIALUP_DISCONNECTED events. The interval 
 140    // parameter is only for Unix where we do the check manually: under 
 141    // Windows, the notification about the change of connection status is 
 144    // Returns FALSE if couldn't set up automatic check for online status. 
 145    virtual bool EnableAutoCheckOnlineStatus(size_t nSeconds
); 
 147    // disable automatic check for connection status change - notice that the 
 148    // wxEVT_DIALUP_XXX events won't be sent any more neither. 
 149    virtual void DisableAutoCheckOnlineStatus(); 
 151    // under Unix, the value of well-known host is used to check whether we're 
 152    // connected to the internet. It's unused under Windows, but this function 
 153    // is always safe to call. The default value is www.yahoo.com. 
 154    virtual void SetWellKnownHost(const wxString
& hostname
, 
 156    /** Sets the commands to start up the network and to hang up 
 157        again. Used by the Unix implementations only. 
 159    virtual void SetConnectCommand(const wxString 
&command
, const wxString 
&hupcmd
) 
 160       { m_ConnectCommand 
= command
; m_HangUpCommand 
= hupcmd
; } 
 162 //private: -- Sun CC 4.2 objects to using NetConnection enum as the return 
 163 //            type if it is declared private 
 165    // the possible results of testing for Online() status 
 168        Net_Unknown 
= -1,    // we couldn't learn anything 
 169        Net_No
,              // no network connection [currently] 
 170        Net_Connected        
// currently connected 
 173    // the possible net connection types 
 176        NetDevice_None    
= 0x0000,  // no network devices (authoritative) 
 177        NetDevice_Unknown 
= 0x0001,  // test doesn't work on this OS 
 178        NetDevice_Modem   
= 0x0002,  // we have a modem 
 179        NetDevice_LAN     
= 0x0004   //         a network card 
 183    // the current status 
 184    NetConnection m_IsOnline
; 
 186    // the connection we have with the network card 
 187    NetConnection m_connCard
; 
 189    // Can we use ifconfig to list active devices? 
 190    int m_CanUseIfconfig
; 
 192    // The path to ifconfig 
 193    wxString m_IfconfigPath
; 
 195    //  Can we use ping to find hosts? 
 197    // The path to ping program 
 201    wxString m_BeaconHost
; 
 202    // beacon host portnumber for connect: 
 205    // command to connect to network 
 206    wxString m_ConnectCommand
; 
 207    // command to hang up 
 208    wxString m_HangUpCommand
; 
 211    // a timer for regular testing 
 212    class AutoCheckTimer 
*m_timer
; 
 213    friend class AutoCheckTimer
; 
 215    // a wxProcess for dialling in background 
 216    class wxDialProcess 
*m_DialProcess
; 
 217    // pid of dial process 
 219    friend class wxDialProcess
; 
 222    void CheckStatus(bool fromAsync 
= FALSE
) const; 
 225    void CheckStatusInternal(); 
 227    // check /proc/net (Linux only) for ppp/eth interfaces, returns the bit 
 228    // mask of NetDeviceType constants 
 231    // check output of ifconfig command for PPP/SLIP/PLIP devices, returns the 
 232    // bit mask of NetDeviceType constants 
 235    // combines the 2 possible checks for determining the connection status 
 236    NetConnection 
CheckConnectAndPing(); 
 239    NetConnection 
CheckPing(); 
 241    // check by connecting to host on given port. 
 242    NetConnection 
CheckConnect(); 
 246 class AutoCheckTimer 
: public wxTimer
 
 249    AutoCheckTimer(wxDialUpManagerImpl 
*dupman
) 
 255    virtual bool Start( int millisecs 
= -1, bool WXUNUSED(one_shot
) = FALSE 
) 
 256       { m_started 
= TRUE
; return wxTimer::Start(millisecs
, FALSE
); } 
 258    virtual void Notify() 
 259       { wxLogTrace(wxT("Checking dial up network status.")); m_dupman
->CheckStatus(); } 
 262       { if ( m_started 
) wxTimer::Stop(); } 
 265    wxDialUpManagerImpl 
*m_dupman
; 
 268 class wxDialProcess 
: public wxProcess
 
 271    wxDialProcess(wxDialUpManagerImpl 
*dupman
) 
 275    void Disconnect() { m_DupMan 
= NULL
; } 
 276    virtual void OnTerminate(int WXUNUSED(pid
), int WXUNUSED(status
)) 
 280             m_DupMan
->m_DialProcess 
= NULL
; 
 281             m_DupMan
->CheckStatus(TRUE
); 
 285       wxDialUpManagerImpl 
*m_DupMan
; 
 289 wxDialUpManagerImpl::wxDialUpManagerImpl() 
 292    m_connCard 
= Net_Unknown
; 
 293    m_DialProcess 
= NULL
; 
 295    m_CanUseIfconfig 
= -1; // unknown 
 296    m_CanUsePing 
= -1; // unknown 
 297    m_BeaconHost 
= WXDIALUP_MANAGER_DEFAULT_BEACONHOST
; 
 301    m_ConnectCommand 
= _T("/usr/etc/ppp"); 
 302 #elif defined(__LINUX__) 
 303    // default values for Debian/GNU linux 
 304    m_ConnectCommand 
= _T("pon"); 
 305    m_HangUpCommand 
= _T("poff"); 
 308    wxChar 
* dial 
= wxGetenv(_T("WXDIALUP_DIALCMD")); 
 309    wxChar 
* hup 
= wxGetenv(_T("WXDIALUP_HUPCMD")); 
 310    SetConnectCommand(dial 
? wxString(dial
) : m_ConnectCommand
, 
 311                      hup 
? wxString(hup
) : m_HangUpCommand
); 
 314 wxDialUpManagerImpl::~wxDialUpManagerImpl() 
 316    if(m_timer
) delete m_timer
; 
 319       m_DialProcess
->Disconnect(); 
 320       m_DialProcess
->Detach(); 
 325 wxDialUpManagerImpl::Dial(const wxString 
&isp
, 
 326                           const wxString 
& WXUNUSED(username
), 
 327                           const wxString 
& WXUNUSED(password
), 
 330    if(m_IsOnline 
== Net_Connected
) 
 334    if(m_ConnectCommand
.Find(wxT("%s"))) 
 335       cmd
.Printf(m_ConnectCommand
,m_ISPname
.c_str()); 
 337       cmd 
= m_ConnectCommand
; 
 341       m_DialProcess 
= new wxDialProcess(this); 
 342       m_DialPId 
= (int)wxExecute(cmd
, FALSE
, m_DialProcess
); 
 345          delete m_DialProcess
; 
 346          m_DialProcess 
= NULL
; 
 353        return wxExecute(cmd
, /* sync */ TRUE
) == 0; 
 356 bool wxDialUpManagerImpl::HangUp() 
 358    if(m_IsOnline 
== Net_No
) 
 362       wxLogError(_("Already dialling ISP.")); 
 366    if(m_HangUpCommand
.Find(wxT("%s"))) 
 367       cmd
.Printf(m_HangUpCommand
,m_ISPname
.c_str(), m_DialProcess
); 
 369       cmd 
= m_HangUpCommand
; 
 370    return wxExecute(cmd
, /* sync */ TRUE
) == 0; 
 374 bool wxDialUpManagerImpl::CancelDialing() 
 378    return kill(m_DialPId
, SIGTERM
) > 0; 
 381 bool wxDialUpManagerImpl::EnableAutoCheckOnlineStatus(size_t nSeconds
) 
 383    DisableAutoCheckOnlineStatus(); 
 384    m_timer 
= new AutoCheckTimer(this); 
 385    bool rc 
= m_timer
->Start(nSeconds
*1000); 
 394 void wxDialUpManagerImpl::DisableAutoCheckOnlineStatus() 
 405 void wxDialUpManagerImpl::SetWellKnownHost(const wxString
& hostname
, int portno
) 
 407    if(hostname
.Length() == 0) 
 409       m_BeaconHost 
= WXDIALUP_MANAGER_DEFAULT_BEACONHOST
; 
 414    // does hostname contain a port number? 
 415    wxString port 
= hostname
.After(wxT(':')); 
 418       m_BeaconHost 
= hostname
.Before(wxT(':')); 
 419       m_BeaconPort 
= wxAtoi(port
); 
 423       m_BeaconHost 
= hostname
; 
 424       m_BeaconPort 
= portno
; 
 429 void wxDialUpManagerImpl::CheckStatus(bool fromAsync
) const 
 431     // This function calls the CheckStatusInternal() helper function 
 432     // which is OS - specific and then sends the events. 
 434     NetConnection oldIsOnline 
= m_IsOnline
; 
 435     ( /* non-const */ (wxDialUpManagerImpl 
*)this)->CheckStatusInternal(); 
 437     // now send the events as appropriate: i.e. if the status changed and 
 438     // if we're in defined state 
 439     if(m_IsOnline 
!= oldIsOnline
 
 440             && m_IsOnline 
!= Net_Unknown
 
 441             && oldIsOnline 
!= Net_Unknown 
) 
 443         wxDialUpEvent 
event(m_IsOnline 
== Net_Connected
, ! fromAsync
); 
 444         (void)wxTheApp
->ProcessEvent(event
); 
 449    We first try to find out if ppp interface is active. If it is, we assume 
 450    that we're online but don't have a permanent connection (this is false if a 
 451    networked machine uses modem to connect to somewhere else, but we can't do 
 452    anything in this case anyhow). 
 454    If no ppp interface is detected, we check for eth interface. If it is 
 455    found, we check that we can, indeed, connect to an Internet host. The logic 
 456    here is that connection check should be fast enough in this case and we 
 457    don't want to give false positives in a (common) case of a machine on a LAN 
 458    which is not connected to the outside. 
 460    If we didn't find either ppp or eth interfaces, we stop here and decide 
 461    that we're connected. However, if couldn't check for this, we try to ping a 
 462    remote host just in case. 
 464    NB1: Checking for the interface presence can be done in 2 ways 
 465         a) reading /proc/net/dev under Linux 
 466         b) spawning ifconfig under any OS 
 468         The first method is faster but only works under Linux. 
 470    NB2: pinging, actually, means that we first try to connect "manually" to 
 471         a port on remove machine and if it fails, we run ping. 
 474 void wxDialUpManagerImpl::CheckStatusInternal() 
 476     m_IsOnline 
= Net_Unknown
; 
 478     // first do quick checks to determine what kind of network devices do we 
 480     int netDeviceType 
= CheckProcNet(); 
 481     if ( netDeviceType 
== NetDevice_Unknown 
) 
 483         // nothing found, try ifconfig too 
 484         netDeviceType 
= CheckIfconfig(); 
 487     switch ( netDeviceType 
) 
 490             // no network devices, no connection 
 495             // we still do ping to confirm that we're connected but we only do 
 496             // it once and hope that the purpose of the network card (i.e. 
 497             // whether it used for connecting to the Internet or just to a 
 498             // LAN) won't change during the program lifetime 
 499             if ( m_connCard 
== Net_Unknown 
) 
 501                 m_connCard 
= CheckConnectAndPing(); 
 503             m_IsOnline 
= m_connCard
; 
 506         case NetDevice_Unknown
: 
 507             // try to ping just in case 
 508             m_IsOnline 
= CheckConnectAndPing(); 
 511         case NetDevice_LAN 
+ NetDevice_Modem
: 
 512         case NetDevice_Modem
: 
 513             // assume we're connected 
 514             m_IsOnline 
= Net_Connected
; 
 518             wxFAIL_MSG(_T("Unexpected netDeviceType")); 
 522 bool wxDialUpManagerImpl::IsAlwaysOnline() const 
 524     wxDialUpManagerImpl 
*self 
= wxConstCast(this, wxDialUpManagerImpl
); 
 526     int netDeviceType 
= self
->CheckProcNet(); 
 527     if ( netDeviceType 
== NetDevice_Unknown 
) 
 529         // nothing found, try ifconfig too 
 530         netDeviceType 
= self
->CheckIfconfig(); 
 533     if ( netDeviceType 
== NetDevice_Unknown 
) 
 535         // this is the only thing we can do unfortunately... 
 541         // we are only permanently online if we have a network card 
 542         return (netDeviceType 
& NetDevice_LAN
) != 0; 
 546 wxDialUpManagerImpl::NetConnection 
wxDialUpManagerImpl::CheckConnectAndPing() 
 550     // first try connecting - faster 
 551     conn 
= CheckConnect(); 
 552     if ( conn 
== Net_Unknown 
) 
 561 wxDialUpManagerImpl::NetConnection 
wxDialUpManagerImpl::CheckConnect() 
 563    // second method: try to connect to a well known host: 
 564    // This can be used under Win 9x, too! 
 566    struct sockaddr_in  serv_addr
; 
 568    if((hp 
= gethostbyname(m_BeaconHost
.mb_str())) == NULL
) 
 569       return Net_No
; // no DNS no net 
 571    serv_addr
.sin_family 
= hp
->h_addrtype
; 
 572    memcpy(&serv_addr
.sin_addr
,hp
->h_addr
, hp
->h_length
); 
 573    serv_addr
.sin_port 
= htons(m_BeaconPort
); 
 576    if( ( sockfd 
= socket(hp
->h_addrtype
, SOCK_STREAM
, 0)) < 0) 
 578       return Net_Unknown
;  // no info 
 581    if( connect(sockfd
, (struct sockaddr 
*) &serv_addr
, 
 582                sizeof(serv_addr
)) >= 0) 
 585       return Net_Connected
; // we can connect, so we have a network! 
 587    else // failed to connect 
 589        if(errno 
== ENETUNREACH
) 
 590           return Net_No
; // network is unreachable 
 592           return Net_Unknown
; // connect failed, but don't know why 
 598 wxDialUpManagerImpl::CheckProcNet() 
 600     // assume that the test doesn't work 
 601     int netDevice 
= NetDevice_Unknown
; 
 604     if (wxFileExists(_T("/proc/net/route"))) 
 606         // cannot use wxFile::Length because file doesn't support seeking, so 
 607         // use stdio directly 
 608         FILE *f 
= fopen("/proc/net/route", "rt"); 
 611             // now we know that we will find all devices we may have 
 612             netDevice 
= NetDevice_None
; 
 616             while (fgets(output
, 256, f
) != NULL
) 
 618                 if ( strstr(output
, "eth") ) // network card 
 620                     netDevice 
|= NetDevice_LAN
; 
 622                 else if (strstr(output
,"ppp")   // ppp 
 623                         || strstr(output
,"sl")  // slip 
 624                         || strstr(output
,"pl")) // plip 
 626                     netDevice 
|= NetDevice_Modem
; 
 640 wxDialUpManagerImpl::CheckIfconfig() 
 643        m_CanUseIfconfig 
= 0; 
 646    // assume that the test doesn't work 
 647     int netDevice 
= NetDevice_Unknown
; 
 649     // first time check for ifconfig location 
 650     if ( m_CanUseIfconfig 
== -1 ) // unknown 
 652         static const wxChar 
*ifconfigLocations
[] = 
 654             _T("/sbin"),         // Linux, FreeBSD 
 655             _T("/usr/sbin"),     // SunOS, Solaris, AIX, HP-UX 
 656             _T("/usr/etc"),      // IRIX 
 659         for ( size_t n 
= 0; n 
< WXSIZEOF(ifconfigLocations
); n
++ ) 
 661             wxString 
path(ifconfigLocations
[n
]); 
 662             path 
<< _T("/ifconfig"); 
 664             if ( wxFileExists(path
) ) 
 666                 m_IfconfigPath 
= path
; 
 672     if ( m_CanUseIfconfig 
!= 0 ) // unknown or yes 
 674         wxLogNull ln
; // suppress all error messages 
 676         wxASSERT_MSG( m_IfconfigPath
.length(), 
 677                       _T("can't use ifconfig if it wasn't found") ); 
 679         wxString tmpfile 
= wxGetTempFileName("_wxdialuptest"); 
 680         wxString cmd 
= "/bin/sh -c \'"; 
 681         cmd 
<< m_IfconfigPath
; 
 682 #if defined(__SOLARIS__) || defined (__SUNOS__) 
 683         // need to add -a flag 
 685 #elif defined(__LINUX__) || defined(__SGI__) 
 686         // nothing to be added to ifconfig 
 687 #elif defined(__FREEBSD__) || defined(__WXMAC__) 
 690 #elif defined(__HPUX__) 
 691         // VZ: a wild guess (but without it, ifconfig fails completely) 
 694 # pragma warning "No ifconfig information for this OS." 
 695        m_CanUseIfconfig 
= 0; 
 698        cmd 
<< " >" << tmpfile 
<<  '\''; 
 699         /* I tried to add an option to wxExecute() to not close stdout, 
 700            so we could let ifconfig write directly to the tmpfile, but 
 701            this does not work. That should be faster, as it doesn´t call 
 702            the shell first. I have no idea why. :-(  (KB) */ 
 703         if ( wxExecute(cmd
,TRUE 
/* sync */) == 0 ) 
 705             m_CanUseIfconfig 
= 1; 
 707             if( file
.Open(tmpfile
) ) 
 710                 if ( file
.ReadAll(&output
) ) 
 712                     // FIXME shouldn't we grep for "^ppp"? (VZ) 
 714                     bool hasModem 
= FALSE
, 
 717 #if defined(__SOLARIS__) || defined (__SUNOS__) 
 718                     // dialup device under SunOS/Solaris 
 719                     hasModem 
= strstr(output
,"ipdptp") != (char *)NULL
; 
 720                     hasLAN 
= strstr(output
, "hme") != (char *)NULL
; 
 721 #elif defined(__LINUX__) || defined (__FREEBSD__) 
 722                     hasModem 
= strstr(output
,"ppp")    // ppp 
 723                         || strstr(output
,"sl")  // slip 
 724                         || strstr(output
,"pl"); // plip 
 725                     hasLAN 
= strstr(output
, "eth") != NULL
; 
 726 #elif defined(__SGI__)  // IRIX 
 727                     hasModem 
= strstr(output
, "ppp") != NULL
; // PPP 
 728 #elif defined(__HPUX__) 
 729                     // if could run ifconfig on interface, then it exists 
 733                     netDevice 
= NetDevice_None
; 
 735                         netDevice 
|= NetDevice_Modem
; 
 737                         netDevice 
|= NetDevice_LAN
; 
 739                 //else: error reading the file 
 741             //else: error opening the file 
 743         else // could not run ifconfig correctly 
 745             m_CanUseIfconfig 
= 0; // don´t try again 
 748         (void) wxRemoveFile(tmpfile
); 
 755 wxDialUpManagerImpl::NetConnection 
wxDialUpManagerImpl::CheckPing() 
 757    // First time check for ping location. We only use the variant 
 758    // which does not take arguments, a la GNU. 
 759    if(m_CanUsePing 
== -1) // unknown 
 762       if(wxFileExists("SYS$SYSTEM:TCPIP$PING.EXE")) 
 763          m_PingPath 
= "$SYS$SYSTEM:TCPIP$PING"; 
 765       if(wxFileExists("/bin/ping")) 
 766          m_PingPath 
= "/bin/ping"; 
 767       else if(wxFileExists("/usr/sbin/ping")) 
 768          m_PingPath 
= "/usr/sbin/ping"; 
 778        // we didn't find ping 
 782    wxLogNull ln
; // suppress all error messages 
 783    wxASSERT(m_PingPath
.length()); 
 785    cmd 
<< m_PingPath 
<< ' '; 
 786 #if defined(__SOLARIS__) || defined (__SUNOS__) 
 787    // nothing to add to ping command 
 788 #elif defined(__LINUX__) || defined ( __FREEBSD__) || defined(__WXMAC__) || defined( __VMS ) 
 789    cmd 
<< "-c 1 "; // only ping once 
 790 #elif defined(__HPUX__) 
 791    cmd 
<< "64 1 "; // only ping once (need also specify the packet size) 
 793 #   pragma warning "No Ping information for this OS." 
 798    if(wxExecute(cmd
, TRUE 
/* sync */) == 0) 
 799       return Net_Connected
; 
 805 wxDialUpManager 
*wxDialUpManager::Create() 
 807    return new wxDialUpManagerImpl
; 
 810 #endif // wxUSE_DIALUP_MANAGER