bobD 6 Posted Sunday at 03:57 PM Basically, program needs to act differently if at home or away--so how do I know? I've looked at using Winsock and HostEnt := gethostbyaddr(@SocketAddr.sin_addr.S_addr, 4, AF_INET); and although that tells me if my home interbase server is available, in a dynamic DHCP environment that IP address isn't necessarily constant. And a call to addr 192.168.1.1 (home router and default DNS gateway) returns nil: the peer computers respond, but the router doesn't. Is there another way to get the name of the local network LAN that works both wired (desktop machines) and wifi? I'd like to try using winsock's gethostbyname but I haven't found a good use example. Or is there a better way? bobD Share this post Link to post
aehimself 402 Posted Sunday at 07:28 PM When you are trying to detect your home network, don't rely on a successful (reverse) DNS lookup. I usually ping my home router (using fqdn) but even better, attempt a connection to a well-known service. However I only use this method to decide if VPN should be fired up or not; it's better to set up your DNS properly so tools will work from inside and outside. If you are keen to go this way I found these two snipplets in my codebase: Function LookupHostName(inIP: String): String; Var host: PHostEnt; addr: Integer; dat: TWSAData; Begin Result := ''; WSAStartup($0101, dat); Try addr := inet_addr(PAnsiChar(AnsiString(inIP))); host := GetHostByAddr(@addr, 4, PF_INET); If host <> nil Then Result := String(host.h_name); Finally WSACleanup; End; End; This one was abandoned halfway as no variables seem to be declared: Write('Attempting to resolve ' + HostName + '...'); If WSAStartup($0101, wsdata) = 0 Then Try tmp := GetHostByName(PAnsiChar(HostName)); If tmp <> nil Then Begin tmpin.sin_addr.S_addr := LongInt(PLongInt(tmp^.h_addr_list^)^); IPAddress := inet_ntoa(tmpin.sin_addr); WriteLn(IPAddress); End; Finally WSACleanup; End; May I ask why your program needs to behave different from a specific network? There might be a better way than relying on simple checks, which might trigger in places you don't want them to. Share this post Link to post
Remy Lebeau 1572 Posted Sunday at 07:39 PM (edited) 3 hours ago, bobD said: Basically, program needs to act differently if at home or away--so how do I know? Ask the user. The OS only knows whether there is a network connection or not. It doesn't care where the network is located or what it's connected to. That's up to the hardware to deal with. Quote Is there another way to get the name of the local network LAN that works both wired (desktop machines) and wifi? No. And as far as sockets are concerned, there is no difference whatsoever whether you are connected to a wired LAN, or to a Wifi, or a cellular provider, etc. The socket API works the same way. The difference is in how the hardware routes the traffic. Quote I'd like to try using winsock's gethostbyname but I haven't found a good use example. Likely because this is not what that API is intended for. Quote Or is there a better way? What EXACTLY are you trying to accomplish in the first place? Edited Sunday at 07:44 PM by Remy Lebeau Share this post Link to post
Brian Evans 117 Posted yesterday at 01:26 AM (edited) There are network profiles in Windows which can be Private, Public or Domain. Normally your home network would be Private and anytime you go elsewhere it would be Public (discovery disabled) or Domain. You could check what the active connection is. Or just key off the active profile name for what network settings your application should use. Snippet below for PowerShell. Get-WmiObject MSFT_NetConnectionProfile -Namespace root/StandardCimv2 | select Name,@{n='ActiveNetworkProfile';e={ switch ($_.NetworkCategory){ 0 {'Public'} 1 {'Private'} 2 {'Domain'} Default {$_.NetworkCategory} } } } Edited yesterday at 01:32 AM by Brian Evans Share this post Link to post
aehimself 402 Posted 18 hours ago 5 hours ago, Brian Evans said: There are network profiles in Windows which can be Private, Public or Domain. Normally your home network would be Private and anytime you go elsewhere it would be Public (discovery disabled) or Domain. You could check what the active connection is. Or just key off the active profile name for what network settings your application should use. This check will trigger on all networks, which are set to private which is probably not a desired behavior. While these will work, OP's solution will be a different design pattern. What if you put the code in a separate DLL, which you are not distributing; and lives only on your home dev PC? Share this post Link to post
bobD 6 Posted 10 hours ago 19 hours ago, Remy Lebeau said: What EXACTLY are you trying to accomplish in the first place? Program is a client/server design can that use either a tcp-ip connection to my home network database, or a local connection to an onboard copy. It should default to network if at home, and default to local if I'm on the road. It can also do things like demand additional authorization, etc. Here's what I've come up with so far: (actually, modified from code I found that you posted some 17 years ago); This isn't as clean as I'd like: rather than throwing away any non-local hits, I'd like a a call to something like gethostbyname that would only check the local 192.168~ address space. Haven't found a way to do that. Currently failure takes longer than success (though not as long as attempting to connect to an IB server that isn't there). unit NetworkPeerAvailable; interface uses winapi.windows, winapi.winsock, System.StrUtils; function PeerAvailable(const aTgtName : string) : boolean; implementation const internalIPMarker = '192.168.'; function PeerAvailable(const aTgtName : string) : boolean; type TaPInAddr = array[0..255] of PInAddr; PaPInAddr = ^TaPInAddr; var WSVersion : Word; WSAData: TWSAData; targetHost: PHostEnt; addr: PaPInAddr; UrlStr : string; begin Result := False; WSVersion := MAKEWORD(2, 2); if WSAStartUp(WSVersion, WSAData) = 0 then try targetHost := gethostbyname(PAnsiChar(AnsiString(aTgtName))); if Assigned(targetHost) then begin addr := PaPInAddr(targetHost^.h_addr_list); if Assigned(addr^[0]) then begin UrlStr := string(inet_ntoa(addr^[0]^)); if StartsText(internalIPMarker, UrlStr) then Result := True; end; end; finally WSACleanUp; end; end; Share this post Link to post
FredS 138 Posted 9 hours ago 25 minutes ago, bobD said: const internalIPMarker = '192.168.'; ..and if your hotel runs an internal network? Share this post Link to post
bobD 6 Posted 8 hours ago As most do <g>, but unlikely then that they'd have a like-named server exposed to their guests, and even where that true, extremely unlikely that my db login credentials would work... So the connection attempt would fail and the program would fall over to the local db. It would just take longer. bobD Share this post Link to post
aehimself 402 Posted 8 hours ago 14 minutes ago, bobD said: So the connection attempt would fail and the program would fall over to the local db. It would just take longer. Why to have any additional checks then...? When you initiate a connection to a host name / fqdn, name resolution is done automatically by the DB client library. Just attempt a connection to your database server. If that fails for any reason, fall back to the local DB. Share this post Link to post
Remy Lebeau 1572 Posted 7 hours ago (edited) 2 hours ago, bobD said: Program is a client/server design can that use either a tcp-ip connection to my home network database, or a local connection to an onboard copy. It should default to network if at home, and default to local if I'm on the road. It can also do things like demand additional authorization, etc. Without having explicit knowledge of your home network setup, there is simply no way for your app to detect whether you are connected to your home network or not. You could try looking for a connected LAN adapter, but what if you are in a hotel that offers LAN access? You could try looking for a connected Wifi adapter, but most public networks are Wifi. You would need your Wifi's SSID to differentiate between a home Wifi and a public Wifi. I think you are making the issue harder then it needs to be. I would suggest taking out the guesswork completely and simply just provide two options in your program's configuration: - connect to a tcp hostname - local copy only And then make it easy to switch between the two configurations as needed. Perhaps wrap them in profiles and then switch the active profile when you leave home and then come home. Another option would be to just skip the local copy altogether and just always connect to your home database server even when you're on the road. Setup port forwarding on your home router and then register a static DynDNS hostname so you can easily find your router's WAN ip/port from public networks (and for added security, setup a VPN server on your home network and then connect to the VPN when on a public network). This way, you always have access to your database server and can connect to it via its IP or hostname on your private LAN. 2 hours ago, bobD said: Here's what I've come up with so far: (actually, modified from code I found that you posted some 17 years ago) If you really want to go down this route, then you need to assign a unique static hostname to your database server, and then you can use gethostbyname() (or better, getaddrinfo()) to lookup that hostname whenever you connect to the database. No need to validate the IP prefix. If you connect to the database by hostname instead of by IP, then you don't even need to perform the lookup yourself, as most TCP libraries/database drivers will handle this task for you. But either way, if the hostname fails to resolve, then you are likely not connected to your home network. Done. Edited 7 hours ago by Remy Lebeau Share this post Link to post
bobD 6 Posted 6 hours ago 1 hour ago, aehimself said: Why to have any additional checks then...? Because a TFDConnection connects very quickly, but takes a long time to fail, and it's annoying to sit and wait <g>. Gethostbyname fails a lot quicker. I do have a checkbox on the login form that allows overriding to local rather than network. This exercise is to see if there's a faster code option to set that check state automatically before the database code is invoked. bobD Share this post Link to post
aehimself 402 Posted 5 hours ago 1 hour ago, bobD said: Because a TFDConnection connects very quickly, but takes a long time to fail, and it's annoying to sit and wait I never personally used FireDac, but doesn't it have a configurable timeout property? If no, what you can do is to attempt to connect in a background thread. Use your local connection until DB connection succeeded then simply swap them out and sync changes. Share this post Link to post
Remy Lebeau 1572 Posted 5 hours ago 1 hour ago, bobD said: Because a TFDConnection connects very quickly, but takes a long time to fail, and it's annoying to sit and wait <g>. Gethostbyname fails a lot quicker. Even if you could lookup the database server's IP quickly, there is no guarantee that the database engine is actually running/responding, so you still have to wait for the full connection before you can do anything with the database, and still handle any failure that may occur. So, may as well just do that by itself, and deal with the slowness of reporting a failure (you should probably file a ticket about that). Share this post Link to post