1 // -*- c++ -*- //////////////////////////////////////////////////////////////// 
   2 // Name:        unix/dialup.cpp 
   3 // Purpose:     Network related wxWidgets classes and functions 
   4 // Author:      Karsten Ballüder 
   8 // Copyright:   (c) Karsten Ballüder 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  13 #   pragma implementation "dialup.h" 
  16 // for compilers that support precompilation, includes "wx.h". 
  17 #include "wx/wxprec.h" 
  21 #if wxUSE_DIALUP_MANAGER 
  27 #include "wx/string.h" 
  29 #include "wx/dialup.h" 
  31 #include "wx/filefn.h" 
  35 #include "wx/process.h" 
  38 #include "wx/wxchar.h" 
  45 #define __STRICT_ANSI__ 
  46 #include <sys/socket.h> 
  48 #include <netinet/in.h> 
  49 #include <arpa/inet.h> 
  52 DEFINE_EVENT_TYPE(wxEVT_DIALUP_CONNECTED
) 
  53 DEFINE_EVENT_TYPE(wxEVT_DIALUP_DISCONNECTED
) 
  55 // ---------------------------------------------------------------------------- 
  56 // A class which groups functions dealing with connecting to the network from a 
  57 // workstation using dial-up access to the net. There is at most one instance 
  58 // of this class in the program accessed via GetDialUpManager(). 
  59 // ---------------------------------------------------------------------------- 
  63  * 1. more configurability for Unix: i.e. how to initiate the connection, how 
  64  *    to check for online status, &c. 
  65  * 2. add a "long Dial(long connectionId = -1)" function which asks the user 
  66  *    about which connection to dial (this may be done using native dialogs 
  67  *    under NT, need generic dialogs for all others) and returns the identifier 
  68  *    of the selected connection (it's opaque to the application) - it may be 
  69  *    reused later to dial the same connection later (or use strings instead of 
  71  * 3. add an async version of dialing functions which notify the caller about 
  72  *    the progress (or may be even start another thread to monitor it) 
  73  * 4. the static creation/accessor functions are not MT-safe - but is this 
  74  *    really crucial? I think we may suppose they're always called from the 
  78 class WXDLLEXPORT wxDialUpManagerImpl 
: public wxDialUpManager
 
  81    wxDialUpManagerImpl(); 
  82    ~wxDialUpManagerImpl(); 
  84    /** Could the dialup manager be initialized correctly? If this function 
  85        returns false, no other functions will work neither, so it's a good idea 
  86        to call this function and check its result before calling any other 
  87        wxDialUpManager methods. 
  89    virtual bool IsOk() const 
  92    /** The simplest way to initiate a dial up: this function dials the given 
  93        ISP (exact meaning of the parameter depends on the platform), returns 
  94        true on success or false on failure and logs the appropriate error 
  95        message in the latter case. 
  96        @param nameOfISP optional paramater for dial program 
  97        @param username unused 
  98        @param password unused 
 100    virtual bool Dial(const wxString
& nameOfISP
, 
 101                      const wxString
& WXUNUSED(username
), 
 102                      const wxString
& WXUNUSED(password
), 
 105    // Hang up the currently active dial up connection. 
 106    virtual bool HangUp(); 
 108    // returns true if the computer is connected to the network: under Windows, 
 109    // this just means that a RAS connection exists, under Unix we check that 
 110    // the "well-known host" (as specified by SetWellKnownHost) is reachable 
 111    virtual bool IsOnline() const 
 114          return m_IsOnline 
== Net_Connected
; 
 117    // do we have a constant net connection? 
 118    virtual bool IsAlwaysOnline() const; 
 120    // returns true if (async) dialing is in progress 
 121    virtual bool IsDialing() const 
 122       { return m_DialProcess 
!= NULL
; } 
 124    // cancel dialing the number initiated with Dial(async = true) 
 125    // NB: this won't result in DISCONNECTED event being sent 
 126    virtual bool CancelDialing(); 
 128    size_t GetISPNames(class wxArrayString 
&) const 
 131    // sometimes the built-in logic for determining the online status may fail, 
 132    // so, in general, the user should be allowed to override it. This function 
 133    // allows to forcefully set the online status - whatever our internal 
 134    // algorithm may think about it. 
 135    virtual void SetOnlineStatus(bool isOnline 
= true) 
 136       { m_IsOnline 
= isOnline 
? Net_Connected 
: Net_No
; } 
 138    // set misc wxDialUpManager options 
 139    // -------------------------------- 
 141    // enable automatical checks for the connection status and sending of 
 142    // wxEVT_DIALUP_CONNECTED/wxEVT_DIALUP_DISCONNECTED events. The interval 
 143    // parameter is only for Unix where we do the check manually: under 
 144    // Windows, the notification about the change of connection status is 
 147    // Returns false if couldn't set up automatic check for online status. 
 148    virtual bool EnableAutoCheckOnlineStatus(size_t nSeconds
); 
 150    // disable automatic check for connection status change - notice that the 
 151    // wxEVT_DIALUP_XXX events won't be sent any more neither. 
 152    virtual void DisableAutoCheckOnlineStatus(); 
 154    // under Unix, the value of well-known host is used to check whether we're 
 155    // connected to the internet. It's unused under Windows, but this function 
 156    // is always safe to call. The default value is www.yahoo.com. 
 157    virtual void SetWellKnownHost(const wxString
& hostname
, 
 159    /** Sets the commands to start up the network and to hang up 
 160        again. Used by the Unix implementations only. 
 162    virtual void SetConnectCommand(const wxString 
&command
, const wxString 
&hupcmd
) 
 163       { m_ConnectCommand 
= command
; m_HangUpCommand 
= hupcmd
; } 
 165 //private: -- Sun CC 4.2 objects to using NetConnection enum as the return 
 166 //            type if it is declared private 
 168    // the possible results of testing for Online() status 
 171        Net_Unknown 
= -1,    // we couldn't learn anything 
 172        Net_No
,              // no network connection [currently] 
 173        Net_Connected        
// currently connected 
 176    // the possible net connection types 
 179        NetDevice_None    
= 0x0000,  // no network devices (authoritative) 
 180        NetDevice_Unknown 
= 0x0001,  // test doesn't work on this OS 
 181        NetDevice_Modem   
= 0x0002,  // we have a modem 
 182        NetDevice_LAN     
= 0x0004   //         a network card 
 186    // the current status 
 187    NetConnection m_IsOnline
; 
 189    // the connection we have with the network card 
 190    NetConnection m_connCard
; 
 192    // Can we use ifconfig to list active devices? 
 193    int m_CanUseIfconfig
; 
 195    // The path to ifconfig 
 196    wxString m_IfconfigPath
; 
 198    //  Can we use ping to find hosts? 
 200    // The path to ping program 
 204    wxString m_BeaconHost
; 
 205    // beacon host portnumber for connect: 
 208    // command to connect to network 
 209    wxString m_ConnectCommand
; 
 210    // command to hang up 
 211    wxString m_HangUpCommand
; 
 214    // a timer for regular testing 
 215    class AutoCheckTimer 
*m_timer
; 
 216    friend class AutoCheckTimer
; 
 218    // a wxProcess for dialling in background 
 219    class wxDialProcess 
*m_DialProcess
; 
 220    // pid of dial process 
 222    friend class wxDialProcess
; 
 225    void CheckStatus(bool fromAsync 
= false) const; 
 228    void CheckStatusInternal(); 
 230    // check /proc/net (Linux only) for ppp/eth interfaces, returns the bit 
 231    // mask of NetDeviceType constants 
 234    // check output of ifconfig command for PPP/SLIP/PLIP devices, returns the 
 235    // bit mask of NetDeviceType constants 
 238    // combines the 2 possible checks for determining the connection status 
 239    NetConnection 
CheckConnectAndPing(); 
 242    NetConnection 
CheckPing(); 
 244    // check by connecting to host on given port. 
 245    NetConnection 
CheckConnect(); 
 249 class AutoCheckTimer 
: public wxTimer
 
 252    AutoCheckTimer(wxDialUpManagerImpl 
*dupman
) 
 257    virtual void Notify() 
 259        wxLogTrace(_T("dialup"), wxT("Checking dial up network status.")); 
 261        m_dupman
->CheckStatus(); 
 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, Darwin 
 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( wxT("_wxdialuptest") ); 
 680         wxString cmd 
= wxT("/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(__DARWIN__) 
 690 #elif defined(__HPUX__) 
 691         // VZ: a wild guess (but without it, ifconfig fails completely) 
 694         #if defined(__GNUG__) 
 695             #warning "No ifconfig information for this OS." 
 697             #pragma warning "No ifconfig information for this OS." 
 700         m_CanUseIfconfig 
= 0; 
 703        cmd 
<< wxT(" >") << tmpfile 
<<  wxT('\''); 
 704         /* I tried to add an option to wxExecute() to not close stdout, 
 705            so we could let ifconfig write directly to the tmpfile, but 
 706            this does not work. That should be faster, as it doesn´t call 
 707            the shell first. I have no idea why. :-(  (KB) */ 
 708         if ( wxExecute(cmd
,true /* sync */) == 0 ) 
 710             m_CanUseIfconfig 
= 1; 
 712             if( file
.Open(tmpfile
) ) 
 715                 if ( file
.ReadAll(&output
) ) 
 717                     // FIXME shouldn't we grep for "^ppp"? (VZ) 
 719                     bool hasModem 
= false, 
 722 #if defined(__SOLARIS__) || defined (__SUNOS__) 
 723                     // dialup device under SunOS/Solaris 
 724                     hasModem 
= strstr(output
.fn_str(),"ipdptp") != (char *)NULL
; 
 725                     hasLAN 
= strstr(output
.fn_str(), "hme") != (char *)NULL
; 
 726 #elif defined(__LINUX__) || defined (__FREEBSD__) 
 727                     hasModem 
= strstr(output
.fn_str(),"ppp")    // ppp 
 728                         || strstr(output
.fn_str(),"sl")  // slip 
 729                         || strstr(output
.fn_str(),"pl"); // plip 
 730                     hasLAN 
= strstr(output
.fn_str(), "eth") != NULL
; 
 731 #elif defined(__SGI__)  // IRIX 
 732                     hasModem 
= strstr(output
, "ppp") != NULL
; // PPP 
 733 #elif defined(__HPUX__) 
 734                     // if could run ifconfig on interface, then it exists 
 738                     netDevice 
= NetDevice_None
; 
 740                         netDevice 
|= NetDevice_Modem
; 
 742                         netDevice 
|= NetDevice_LAN
; 
 744                 //else: error reading the file 
 746             //else: error opening the file 
 748         else // could not run ifconfig correctly 
 750             m_CanUseIfconfig 
= 0; // don´t try again 
 753         (void) wxRemoveFile(tmpfile
); 
 760 wxDialUpManagerImpl::NetConnection 
wxDialUpManagerImpl::CheckPing() 
 762     // First time check for ping location. We only use the variant 
 763     // which does not take arguments, a la GNU. 
 764     if(m_CanUsePing 
== -1) // unknown 
 767         if (wxFileExists( wxT("SYS$SYSTEM:TCPIP$PING.EXE") )) 
 768             m_PingPath 
= wxT("$SYS$SYSTEM:TCPIP$PING"); 
 770         if (wxFileExists( wxT("/bin/ping") )) 
 771             m_PingPath 
= wxT("/bin/ping"); 
 772         else if (wxFileExists( wxT("/usr/sbin/ping") )) 
 773             m_PingPath 
= wxT("/usr/sbin/ping"); 
 783        // we didn't find ping 
 787     wxLogNull ln
; // suppress all error messages 
 788     wxASSERT(m_PingPath
.length()); 
 790     cmd 
<< m_PingPath 
<< wxT(' '); 
 791 #if defined(__SOLARIS__) || defined (__SUNOS__) 
 792     // nothing to add to ping command 
 793 #elif defined(__LINUX__) || defined (__BSD__) || defined( __VMS ) 
 794     cmd 
<< wxT("-c 1 "); // only ping once 
 795 #elif defined(__HPUX__) 
 796     cmd 
<< wxT("64 1 "); // only ping once (need also specify the packet size) 
 798     #if defined(__GNUG__) 
 799         #warning "No Ping information for this OS." 
 801         #pragma warning "No Ping information for this OS." 
 808     if(wxExecute(cmd
, true /* sync */) == 0) 
 809         return Net_Connected
; 
 815 wxDialUpManager 
*wxDialUpManager::Create() 
 817    return new wxDialUpManagerImpl
; 
 820 #endif // wxUSE_DIALUP_MANAGER