Why isn't Android's Captive Portal detection triggering a browser window?












2















I have a Raspberry Pi that is hosting a simple website using nginx. The
RPi is acting as a Wireless Access Point - users can connect to its wireless
network, the RPi gives them an IP (it runs a DHCP server), and they can
access the site.



Because the RPi doesn't actually provide users with internet (only this one site), I have made it easier for users to find the site. Instead of knowing the exact URL for the site, I have told my dns server (dnsmasq) which the DHCP server tells clients to use, to resolve all queries to the LAN IP of my RPi (192.168.30.1).



At this point, my nginx has an entry in its config that says:




  • if the host field of the user's request is not MyRPiServer.com, send a 302 redirect to MyRPiServer.com

  • if the host is MyRPiServer.com, serve the local website


This works awesome.



I wanted to take it one step further. When Android connects to a wireless network,
it tries to connect to http://connectivitycheck.gstatic.com/generate_204 (or one of the other similar google pages) specifically to check if the request is being redirected. If it gets a 204 code, it assumes everything is fine. If it does not, it assumes it is behind a captive portal, and pops up a browser window that opens the captive portal login.



For some reason, when I tell nginx to respond to requests for the generate_204 page with either a 302 redirect, or even a 200 (with some text), Android doesn't popup the browser.



I have a mikrotik router with a built-in hotspot feature, which does indeed have android popup the browser with the captive portal login (on the same test phone). When I look at the traffic it sends my client, it is a simple HTTP 200, with some text, just like mine.



The one thing that does seem to work is if I disable my DNS server from resolving everything to 192.168.30.1, and use iptables to redirect port 80 to localhost on my RPi.



Does anyone know why redirecting port 80 works when it comes to Android's Captive Portal detection, but configuring the DNS server to resolve everything to the RPi local IP doesn't?



Looking at the code found here https://stackoverflow.com/a/14030276/4258196, it appears that the only thing Android cares about is if it can connect to the
host, and if it gets an HTTP 204 back. In my case, it's definitely connecting,
and it's definitely not getting a 204 back (nginx logs show it sending HTTP
302 and HTTP 200).



My phone is running Android 8, so the code linked might be different now I supposed.










share|improve this question























  • see unix.stackexchange.com/questions/212871/…

    – Rui F Ribeiro
    Mar 19 '18 at 23:09











  • Setting my nginx to listen on port 443, and provide a self-signed SSL cert had no effect on the Android window popup - it still doesn't appear. It also doesn't make sense that Android Captive Portal detection would force you to use SSL, and then show you an SSL error because you are redirecting SSL.

    – Tal
    Mar 20 '18 at 15:08













  • "...when having Chrome installed" the behaviour changes. Found a lot of posts about it, and confirmed it on the field, after a workmate with android 8 was not able to open the captive portal. We have a couple of ISPes that provide roaming services for their customers (we) we captive portals, and they use valid public certificates for it.

    – Rui F Ribeiro
    Mar 20 '18 at 16:45













  • I don't fully understand why. Even if you have a valid certificate on your captive portal login page, if the user is trying to get to https://gmail.com, and they get your valid certificate intstead, the browser will still show them a cert warning since that's not the cert it expected. My test phone does have Chrome installed (came pre-installed), but the Captive Portal popup android uses doesn't use Chrome - not sure if that matters. Either way - do you know exactly what Android is looking for when doing paywall detection (with Chrome installed)?

    – Tal
    Mar 20 '18 at 17:43













  • did tcpdumps at the time, and had the Apache logs, but I think I forgot the tcpdumps in my Mac when left my job; I might have some Apache logs, not sure. I am not looking to specific data/URLs nowadays, I just redirect whatever it happens to the captive portal. I am in my home town these days, but in a week time might do some tests if need be.

    – Rui F Ribeiro
    Mar 20 '18 at 17:44


















2















I have a Raspberry Pi that is hosting a simple website using nginx. The
RPi is acting as a Wireless Access Point - users can connect to its wireless
network, the RPi gives them an IP (it runs a DHCP server), and they can
access the site.



Because the RPi doesn't actually provide users with internet (only this one site), I have made it easier for users to find the site. Instead of knowing the exact URL for the site, I have told my dns server (dnsmasq) which the DHCP server tells clients to use, to resolve all queries to the LAN IP of my RPi (192.168.30.1).



At this point, my nginx has an entry in its config that says:




  • if the host field of the user's request is not MyRPiServer.com, send a 302 redirect to MyRPiServer.com

  • if the host is MyRPiServer.com, serve the local website


This works awesome.



I wanted to take it one step further. When Android connects to a wireless network,
it tries to connect to http://connectivitycheck.gstatic.com/generate_204 (or one of the other similar google pages) specifically to check if the request is being redirected. If it gets a 204 code, it assumes everything is fine. If it does not, it assumes it is behind a captive portal, and pops up a browser window that opens the captive portal login.



For some reason, when I tell nginx to respond to requests for the generate_204 page with either a 302 redirect, or even a 200 (with some text), Android doesn't popup the browser.



I have a mikrotik router with a built-in hotspot feature, which does indeed have android popup the browser with the captive portal login (on the same test phone). When I look at the traffic it sends my client, it is a simple HTTP 200, with some text, just like mine.



The one thing that does seem to work is if I disable my DNS server from resolving everything to 192.168.30.1, and use iptables to redirect port 80 to localhost on my RPi.



Does anyone know why redirecting port 80 works when it comes to Android's Captive Portal detection, but configuring the DNS server to resolve everything to the RPi local IP doesn't?



Looking at the code found here https://stackoverflow.com/a/14030276/4258196, it appears that the only thing Android cares about is if it can connect to the
host, and if it gets an HTTP 204 back. In my case, it's definitely connecting,
and it's definitely not getting a 204 back (nginx logs show it sending HTTP
302 and HTTP 200).



My phone is running Android 8, so the code linked might be different now I supposed.










share|improve this question























  • see unix.stackexchange.com/questions/212871/…

    – Rui F Ribeiro
    Mar 19 '18 at 23:09











  • Setting my nginx to listen on port 443, and provide a self-signed SSL cert had no effect on the Android window popup - it still doesn't appear. It also doesn't make sense that Android Captive Portal detection would force you to use SSL, and then show you an SSL error because you are redirecting SSL.

    – Tal
    Mar 20 '18 at 15:08













  • "...when having Chrome installed" the behaviour changes. Found a lot of posts about it, and confirmed it on the field, after a workmate with android 8 was not able to open the captive portal. We have a couple of ISPes that provide roaming services for their customers (we) we captive portals, and they use valid public certificates for it.

    – Rui F Ribeiro
    Mar 20 '18 at 16:45













  • I don't fully understand why. Even if you have a valid certificate on your captive portal login page, if the user is trying to get to https://gmail.com, and they get your valid certificate intstead, the browser will still show them a cert warning since that's not the cert it expected. My test phone does have Chrome installed (came pre-installed), but the Captive Portal popup android uses doesn't use Chrome - not sure if that matters. Either way - do you know exactly what Android is looking for when doing paywall detection (with Chrome installed)?

    – Tal
    Mar 20 '18 at 17:43













  • did tcpdumps at the time, and had the Apache logs, but I think I forgot the tcpdumps in my Mac when left my job; I might have some Apache logs, not sure. I am not looking to specific data/URLs nowadays, I just redirect whatever it happens to the captive portal. I am in my home town these days, but in a week time might do some tests if need be.

    – Rui F Ribeiro
    Mar 20 '18 at 17:44
















2












2








2


1






I have a Raspberry Pi that is hosting a simple website using nginx. The
RPi is acting as a Wireless Access Point - users can connect to its wireless
network, the RPi gives them an IP (it runs a DHCP server), and they can
access the site.



Because the RPi doesn't actually provide users with internet (only this one site), I have made it easier for users to find the site. Instead of knowing the exact URL for the site, I have told my dns server (dnsmasq) which the DHCP server tells clients to use, to resolve all queries to the LAN IP of my RPi (192.168.30.1).



At this point, my nginx has an entry in its config that says:




  • if the host field of the user's request is not MyRPiServer.com, send a 302 redirect to MyRPiServer.com

  • if the host is MyRPiServer.com, serve the local website


This works awesome.



I wanted to take it one step further. When Android connects to a wireless network,
it tries to connect to http://connectivitycheck.gstatic.com/generate_204 (or one of the other similar google pages) specifically to check if the request is being redirected. If it gets a 204 code, it assumes everything is fine. If it does not, it assumes it is behind a captive portal, and pops up a browser window that opens the captive portal login.



For some reason, when I tell nginx to respond to requests for the generate_204 page with either a 302 redirect, or even a 200 (with some text), Android doesn't popup the browser.



I have a mikrotik router with a built-in hotspot feature, which does indeed have android popup the browser with the captive portal login (on the same test phone). When I look at the traffic it sends my client, it is a simple HTTP 200, with some text, just like mine.



The one thing that does seem to work is if I disable my DNS server from resolving everything to 192.168.30.1, and use iptables to redirect port 80 to localhost on my RPi.



Does anyone know why redirecting port 80 works when it comes to Android's Captive Portal detection, but configuring the DNS server to resolve everything to the RPi local IP doesn't?



Looking at the code found here https://stackoverflow.com/a/14030276/4258196, it appears that the only thing Android cares about is if it can connect to the
host, and if it gets an HTTP 204 back. In my case, it's definitely connecting,
and it's definitely not getting a 204 back (nginx logs show it sending HTTP
302 and HTTP 200).



My phone is running Android 8, so the code linked might be different now I supposed.










share|improve this question














I have a Raspberry Pi that is hosting a simple website using nginx. The
RPi is acting as a Wireless Access Point - users can connect to its wireless
network, the RPi gives them an IP (it runs a DHCP server), and they can
access the site.



Because the RPi doesn't actually provide users with internet (only this one site), I have made it easier for users to find the site. Instead of knowing the exact URL for the site, I have told my dns server (dnsmasq) which the DHCP server tells clients to use, to resolve all queries to the LAN IP of my RPi (192.168.30.1).



At this point, my nginx has an entry in its config that says:




  • if the host field of the user's request is not MyRPiServer.com, send a 302 redirect to MyRPiServer.com

  • if the host is MyRPiServer.com, serve the local website


This works awesome.



I wanted to take it one step further. When Android connects to a wireless network,
it tries to connect to http://connectivitycheck.gstatic.com/generate_204 (or one of the other similar google pages) specifically to check if the request is being redirected. If it gets a 204 code, it assumes everything is fine. If it does not, it assumes it is behind a captive portal, and pops up a browser window that opens the captive portal login.



For some reason, when I tell nginx to respond to requests for the generate_204 page with either a 302 redirect, or even a 200 (with some text), Android doesn't popup the browser.



I have a mikrotik router with a built-in hotspot feature, which does indeed have android popup the browser with the captive portal login (on the same test phone). When I look at the traffic it sends my client, it is a simple HTTP 200, with some text, just like mine.



The one thing that does seem to work is if I disable my DNS server from resolving everything to 192.168.30.1, and use iptables to redirect port 80 to localhost on my RPi.



Does anyone know why redirecting port 80 works when it comes to Android's Captive Portal detection, but configuring the DNS server to resolve everything to the RPi local IP doesn't?



Looking at the code found here https://stackoverflow.com/a/14030276/4258196, it appears that the only thing Android cares about is if it can connect to the
host, and if it gets an HTTP 204 back. In my case, it's definitely connecting,
and it's definitely not getting a 204 back (nginx logs show it sending HTTP
302 and HTTP 200).



My phone is running Android 8, so the code linked might be different now I supposed.







nginx webserver android http






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Mar 19 '18 at 21:42









TalTal

6611923




6611923













  • see unix.stackexchange.com/questions/212871/…

    – Rui F Ribeiro
    Mar 19 '18 at 23:09











  • Setting my nginx to listen on port 443, and provide a self-signed SSL cert had no effect on the Android window popup - it still doesn't appear. It also doesn't make sense that Android Captive Portal detection would force you to use SSL, and then show you an SSL error because you are redirecting SSL.

    – Tal
    Mar 20 '18 at 15:08













  • "...when having Chrome installed" the behaviour changes. Found a lot of posts about it, and confirmed it on the field, after a workmate with android 8 was not able to open the captive portal. We have a couple of ISPes that provide roaming services for their customers (we) we captive portals, and they use valid public certificates for it.

    – Rui F Ribeiro
    Mar 20 '18 at 16:45













  • I don't fully understand why. Even if you have a valid certificate on your captive portal login page, if the user is trying to get to https://gmail.com, and they get your valid certificate intstead, the browser will still show them a cert warning since that's not the cert it expected. My test phone does have Chrome installed (came pre-installed), but the Captive Portal popup android uses doesn't use Chrome - not sure if that matters. Either way - do you know exactly what Android is looking for when doing paywall detection (with Chrome installed)?

    – Tal
    Mar 20 '18 at 17:43













  • did tcpdumps at the time, and had the Apache logs, but I think I forgot the tcpdumps in my Mac when left my job; I might have some Apache logs, not sure. I am not looking to specific data/URLs nowadays, I just redirect whatever it happens to the captive portal. I am in my home town these days, but in a week time might do some tests if need be.

    – Rui F Ribeiro
    Mar 20 '18 at 17:44





















  • see unix.stackexchange.com/questions/212871/…

    – Rui F Ribeiro
    Mar 19 '18 at 23:09











  • Setting my nginx to listen on port 443, and provide a self-signed SSL cert had no effect on the Android window popup - it still doesn't appear. It also doesn't make sense that Android Captive Portal detection would force you to use SSL, and then show you an SSL error because you are redirecting SSL.

    – Tal
    Mar 20 '18 at 15:08













  • "...when having Chrome installed" the behaviour changes. Found a lot of posts about it, and confirmed it on the field, after a workmate with android 8 was not able to open the captive portal. We have a couple of ISPes that provide roaming services for their customers (we) we captive portals, and they use valid public certificates for it.

    – Rui F Ribeiro
    Mar 20 '18 at 16:45













  • I don't fully understand why. Even if you have a valid certificate on your captive portal login page, if the user is trying to get to https://gmail.com, and they get your valid certificate intstead, the browser will still show them a cert warning since that's not the cert it expected. My test phone does have Chrome installed (came pre-installed), but the Captive Portal popup android uses doesn't use Chrome - not sure if that matters. Either way - do you know exactly what Android is looking for when doing paywall detection (with Chrome installed)?

    – Tal
    Mar 20 '18 at 17:43













  • did tcpdumps at the time, and had the Apache logs, but I think I forgot the tcpdumps in my Mac when left my job; I might have some Apache logs, not sure. I am not looking to specific data/URLs nowadays, I just redirect whatever it happens to the captive portal. I am in my home town these days, but in a week time might do some tests if need be.

    – Rui F Ribeiro
    Mar 20 '18 at 17:44



















see unix.stackexchange.com/questions/212871/…

– Rui F Ribeiro
Mar 19 '18 at 23:09





see unix.stackexchange.com/questions/212871/…

– Rui F Ribeiro
Mar 19 '18 at 23:09













Setting my nginx to listen on port 443, and provide a self-signed SSL cert had no effect on the Android window popup - it still doesn't appear. It also doesn't make sense that Android Captive Portal detection would force you to use SSL, and then show you an SSL error because you are redirecting SSL.

– Tal
Mar 20 '18 at 15:08







Setting my nginx to listen on port 443, and provide a self-signed SSL cert had no effect on the Android window popup - it still doesn't appear. It also doesn't make sense that Android Captive Portal detection would force you to use SSL, and then show you an SSL error because you are redirecting SSL.

– Tal
Mar 20 '18 at 15:08















"...when having Chrome installed" the behaviour changes. Found a lot of posts about it, and confirmed it on the field, after a workmate with android 8 was not able to open the captive portal. We have a couple of ISPes that provide roaming services for their customers (we) we captive portals, and they use valid public certificates for it.

– Rui F Ribeiro
Mar 20 '18 at 16:45







"...when having Chrome installed" the behaviour changes. Found a lot of posts about it, and confirmed it on the field, after a workmate with android 8 was not able to open the captive portal. We have a couple of ISPes that provide roaming services for their customers (we) we captive portals, and they use valid public certificates for it.

– Rui F Ribeiro
Mar 20 '18 at 16:45















I don't fully understand why. Even if you have a valid certificate on your captive portal login page, if the user is trying to get to https://gmail.com, and they get your valid certificate intstead, the browser will still show them a cert warning since that's not the cert it expected. My test phone does have Chrome installed (came pre-installed), but the Captive Portal popup android uses doesn't use Chrome - not sure if that matters. Either way - do you know exactly what Android is looking for when doing paywall detection (with Chrome installed)?

– Tal
Mar 20 '18 at 17:43







I don't fully understand why. Even if you have a valid certificate on your captive portal login page, if the user is trying to get to https://gmail.com, and they get your valid certificate intstead, the browser will still show them a cert warning since that's not the cert it expected. My test phone does have Chrome installed (came pre-installed), but the Captive Portal popup android uses doesn't use Chrome - not sure if that matters. Either way - do you know exactly what Android is looking for when doing paywall detection (with Chrome installed)?

– Tal
Mar 20 '18 at 17:43















did tcpdumps at the time, and had the Apache logs, but I think I forgot the tcpdumps in my Mac when left my job; I might have some Apache logs, not sure. I am not looking to specific data/URLs nowadays, I just redirect whatever it happens to the captive portal. I am in my home town these days, but in a week time might do some tests if need be.

– Rui F Ribeiro
Mar 20 '18 at 17:44







did tcpdumps at the time, and had the Apache logs, but I think I forgot the tcpdumps in my Mac when left my job; I might have some Apache logs, not sure. I am not looking to specific data/URLs nowadays, I just redirect whatever it happens to the captive portal. I am in my home town these days, but in a week time might do some tests if need be.

– Rui F Ribeiro
Mar 20 '18 at 17:44












3 Answers
3






active

oldest

votes


















2














The solution I found was to set my dnsmasq to continue to resolve everything to 192.168.30.1, but to have some exceptions for captive portal test servers:



10.45.12.1 clients3.google.com
10.45.12.1 clients.l.google.com
10.45.12.1 connectivitycheck.android.com
10.45.12.1 connectivitycheck.gstatic.com
10.45.12.1 play.googleapis.com


Basically if anything tries to resolve the above domains using our dns server, they get a reply of 10.45.12.1.



10.45.12.1 is a random IP that doesn't belong to anything. It just has to not be 192.168.30.1.



The list of domains came from here.



With this in place, as soon as you connect to the RPi's WiFi, it pops up the browser page showing my site.



This is a solution, but not really an answer to the question of why this happens. If anyone can explain it, I would appreciate it.



Edit:



With this solution, if I connect and disconnect from the WiFi on the device several times, sometimes Android pops up the login page, and sometimes it doesn't. For anyone doing something similar, in the end, for a better solution, I went with this:




  • Have DNSmasq resolve everything to 10.45.12.1 (or anything outside of the 192.168.30.0/24 subnet)


    • It MUST be outside of the 192.168.30.0/24 subnet (or whatever your LAN subnet is) or the client will try to use ARP to figure out the MAC address of the given device, and will fail, because the device doesn't actually exist



  • Have iptables forward port 80 coming from the wifi interface to localhost


This works for Android, OS X, and Windows. I don't have an iOS device to test this with. According to this, iOS devices may require some additional work.



I'm still curious why this was even necessary, and why resolving everything to 192.168.30.1 didn't work in the first place.






share|improve this answer


























  • Pretty interesting stuff. Still trying to think why.

    – Rui F Ribeiro
    Mar 20 '18 at 21:44













  • Have you tried to return a HTTP Status 204 with any address that requrests the URLs /generate_204 and /gen_204 ?

    – Rui F Ribeiro
    Mar 20 '18 at 21:48













  • I haven't tried it, but that's what Android is looking for as a confirmation that everything is fine, isn't it? That's what you do if you have a captive portal setup, but don't want android to know about it (to popup anything).

    – Tal
    Mar 21 '18 at 1:38











  • Hi I suppose when you resolved everything to one host it is remotely possible that nginx was actually giving the expected responses (204) hence no need for captive portal to pop up.

    – r0berts
    Aug 4 '18 at 19:01



















0














I spent a lot of time trying to figure this out and finally did it. You can see the code here https://github.com/tretos53/Captive-Portal



It's a combination of domains pointing to public IPs, iptables and nginx redirects.






share|improve this answer
























  • Hello and welcome to the U&L stack exchange site! Please review the Help Center to get information on how to best post to this site. To get to your answer, please consider editing it to add additional context. While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. Thank you!

    – kemotep
    Nov 27 '18 at 13:26





















0














There's a lot of different reasons for this, could be your specific hardware phone manufacturer as well. I currently built my Captive Portal library for Mongoose OS responding to all DNS queries with device IP and don't have any problems with devices besides Samsung devices which seem to require the 200 response with text.



Here's the library stack:
https://github.com/tripflex/captive-portal-wifi-stack



Specifically here's the Captive Portal handling, along with details of the endpoints I use, and details in the README on how it's handled:



https://github.com/tripflex/captive-portal



You will see the one solution I used for Samsung devices is to return a 200 with a generated HTML files using a meta refresh tag:



https://github.com/tripflex/captive-portal#cportalredirect_file-setting



<html>
<head>
<title>Redirecting to Captive Portal</title>
<meta http-equiv='refresh' content='0; url=PORTAL_URL'>
</head>
<body>
<p>Please wait, refreshing. If page does not refresh, click <a href='PORTAL_URL'>here</a> to login.</p>
</body>
</html>


But really the main thing was setting up endpoint handlings for each specific device:



https://github.com/tripflex/captive-portal#known-endpoints





  • /mobile/status.php Android 8.0 (Samsung s9+)


  • /generate_204 Android


  • /gen_204 Android


  • /ncsi.txt Windows


  • /hotspot-detect.html iOS/OSX


  • /hotspotdetect.html iOS/OSX


  • /library/test/success.html iOS


  • /success.txt OSX


  • /kindle-wifi/wifiredirect.html Kindle when requested with com.android.captiveportallogin


  • /kindle-wifi/wifistub.html Kindle before requesting with captive portal login window (maybe for detection?)






share|improve this answer























    Your Answer








    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "106"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f432190%2fwhy-isnt-androids-captive-portal-detection-triggering-a-browser-window%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    2














    The solution I found was to set my dnsmasq to continue to resolve everything to 192.168.30.1, but to have some exceptions for captive portal test servers:



    10.45.12.1 clients3.google.com
    10.45.12.1 clients.l.google.com
    10.45.12.1 connectivitycheck.android.com
    10.45.12.1 connectivitycheck.gstatic.com
    10.45.12.1 play.googleapis.com


    Basically if anything tries to resolve the above domains using our dns server, they get a reply of 10.45.12.1.



    10.45.12.1 is a random IP that doesn't belong to anything. It just has to not be 192.168.30.1.



    The list of domains came from here.



    With this in place, as soon as you connect to the RPi's WiFi, it pops up the browser page showing my site.



    This is a solution, but not really an answer to the question of why this happens. If anyone can explain it, I would appreciate it.



    Edit:



    With this solution, if I connect and disconnect from the WiFi on the device several times, sometimes Android pops up the login page, and sometimes it doesn't. For anyone doing something similar, in the end, for a better solution, I went with this:




    • Have DNSmasq resolve everything to 10.45.12.1 (or anything outside of the 192.168.30.0/24 subnet)


      • It MUST be outside of the 192.168.30.0/24 subnet (or whatever your LAN subnet is) or the client will try to use ARP to figure out the MAC address of the given device, and will fail, because the device doesn't actually exist



    • Have iptables forward port 80 coming from the wifi interface to localhost


    This works for Android, OS X, and Windows. I don't have an iOS device to test this with. According to this, iOS devices may require some additional work.



    I'm still curious why this was even necessary, and why resolving everything to 192.168.30.1 didn't work in the first place.






    share|improve this answer


























    • Pretty interesting stuff. Still trying to think why.

      – Rui F Ribeiro
      Mar 20 '18 at 21:44













    • Have you tried to return a HTTP Status 204 with any address that requrests the URLs /generate_204 and /gen_204 ?

      – Rui F Ribeiro
      Mar 20 '18 at 21:48













    • I haven't tried it, but that's what Android is looking for as a confirmation that everything is fine, isn't it? That's what you do if you have a captive portal setup, but don't want android to know about it (to popup anything).

      – Tal
      Mar 21 '18 at 1:38











    • Hi I suppose when you resolved everything to one host it is remotely possible that nginx was actually giving the expected responses (204) hence no need for captive portal to pop up.

      – r0berts
      Aug 4 '18 at 19:01
















    2














    The solution I found was to set my dnsmasq to continue to resolve everything to 192.168.30.1, but to have some exceptions for captive portal test servers:



    10.45.12.1 clients3.google.com
    10.45.12.1 clients.l.google.com
    10.45.12.1 connectivitycheck.android.com
    10.45.12.1 connectivitycheck.gstatic.com
    10.45.12.1 play.googleapis.com


    Basically if anything tries to resolve the above domains using our dns server, they get a reply of 10.45.12.1.



    10.45.12.1 is a random IP that doesn't belong to anything. It just has to not be 192.168.30.1.



    The list of domains came from here.



    With this in place, as soon as you connect to the RPi's WiFi, it pops up the browser page showing my site.



    This is a solution, but not really an answer to the question of why this happens. If anyone can explain it, I would appreciate it.



    Edit:



    With this solution, if I connect and disconnect from the WiFi on the device several times, sometimes Android pops up the login page, and sometimes it doesn't. For anyone doing something similar, in the end, for a better solution, I went with this:




    • Have DNSmasq resolve everything to 10.45.12.1 (or anything outside of the 192.168.30.0/24 subnet)


      • It MUST be outside of the 192.168.30.0/24 subnet (or whatever your LAN subnet is) or the client will try to use ARP to figure out the MAC address of the given device, and will fail, because the device doesn't actually exist



    • Have iptables forward port 80 coming from the wifi interface to localhost


    This works for Android, OS X, and Windows. I don't have an iOS device to test this with. According to this, iOS devices may require some additional work.



    I'm still curious why this was even necessary, and why resolving everything to 192.168.30.1 didn't work in the first place.






    share|improve this answer


























    • Pretty interesting stuff. Still trying to think why.

      – Rui F Ribeiro
      Mar 20 '18 at 21:44













    • Have you tried to return a HTTP Status 204 with any address that requrests the URLs /generate_204 and /gen_204 ?

      – Rui F Ribeiro
      Mar 20 '18 at 21:48













    • I haven't tried it, but that's what Android is looking for as a confirmation that everything is fine, isn't it? That's what you do if you have a captive portal setup, but don't want android to know about it (to popup anything).

      – Tal
      Mar 21 '18 at 1:38











    • Hi I suppose when you resolved everything to one host it is remotely possible that nginx was actually giving the expected responses (204) hence no need for captive portal to pop up.

      – r0berts
      Aug 4 '18 at 19:01














    2












    2








    2







    The solution I found was to set my dnsmasq to continue to resolve everything to 192.168.30.1, but to have some exceptions for captive portal test servers:



    10.45.12.1 clients3.google.com
    10.45.12.1 clients.l.google.com
    10.45.12.1 connectivitycheck.android.com
    10.45.12.1 connectivitycheck.gstatic.com
    10.45.12.1 play.googleapis.com


    Basically if anything tries to resolve the above domains using our dns server, they get a reply of 10.45.12.1.



    10.45.12.1 is a random IP that doesn't belong to anything. It just has to not be 192.168.30.1.



    The list of domains came from here.



    With this in place, as soon as you connect to the RPi's WiFi, it pops up the browser page showing my site.



    This is a solution, but not really an answer to the question of why this happens. If anyone can explain it, I would appreciate it.



    Edit:



    With this solution, if I connect and disconnect from the WiFi on the device several times, sometimes Android pops up the login page, and sometimes it doesn't. For anyone doing something similar, in the end, for a better solution, I went with this:




    • Have DNSmasq resolve everything to 10.45.12.1 (or anything outside of the 192.168.30.0/24 subnet)


      • It MUST be outside of the 192.168.30.0/24 subnet (or whatever your LAN subnet is) or the client will try to use ARP to figure out the MAC address of the given device, and will fail, because the device doesn't actually exist



    • Have iptables forward port 80 coming from the wifi interface to localhost


    This works for Android, OS X, and Windows. I don't have an iOS device to test this with. According to this, iOS devices may require some additional work.



    I'm still curious why this was even necessary, and why resolving everything to 192.168.30.1 didn't work in the first place.






    share|improve this answer















    The solution I found was to set my dnsmasq to continue to resolve everything to 192.168.30.1, but to have some exceptions for captive portal test servers:



    10.45.12.1 clients3.google.com
    10.45.12.1 clients.l.google.com
    10.45.12.1 connectivitycheck.android.com
    10.45.12.1 connectivitycheck.gstatic.com
    10.45.12.1 play.googleapis.com


    Basically if anything tries to resolve the above domains using our dns server, they get a reply of 10.45.12.1.



    10.45.12.1 is a random IP that doesn't belong to anything. It just has to not be 192.168.30.1.



    The list of domains came from here.



    With this in place, as soon as you connect to the RPi's WiFi, it pops up the browser page showing my site.



    This is a solution, but not really an answer to the question of why this happens. If anyone can explain it, I would appreciate it.



    Edit:



    With this solution, if I connect and disconnect from the WiFi on the device several times, sometimes Android pops up the login page, and sometimes it doesn't. For anyone doing something similar, in the end, for a better solution, I went with this:




    • Have DNSmasq resolve everything to 10.45.12.1 (or anything outside of the 192.168.30.0/24 subnet)


      • It MUST be outside of the 192.168.30.0/24 subnet (or whatever your LAN subnet is) or the client will try to use ARP to figure out the MAC address of the given device, and will fail, because the device doesn't actually exist



    • Have iptables forward port 80 coming from the wifi interface to localhost


    This works for Android, OS X, and Windows. I don't have an iOS device to test this with. According to this, iOS devices may require some additional work.



    I'm still curious why this was even necessary, and why resolving everything to 192.168.30.1 didn't work in the first place.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Mar 23 '18 at 18:21

























    answered Mar 20 '18 at 21:36









    TalTal

    6611923




    6611923













    • Pretty interesting stuff. Still trying to think why.

      – Rui F Ribeiro
      Mar 20 '18 at 21:44













    • Have you tried to return a HTTP Status 204 with any address that requrests the URLs /generate_204 and /gen_204 ?

      – Rui F Ribeiro
      Mar 20 '18 at 21:48













    • I haven't tried it, but that's what Android is looking for as a confirmation that everything is fine, isn't it? That's what you do if you have a captive portal setup, but don't want android to know about it (to popup anything).

      – Tal
      Mar 21 '18 at 1:38











    • Hi I suppose when you resolved everything to one host it is remotely possible that nginx was actually giving the expected responses (204) hence no need for captive portal to pop up.

      – r0berts
      Aug 4 '18 at 19:01



















    • Pretty interesting stuff. Still trying to think why.

      – Rui F Ribeiro
      Mar 20 '18 at 21:44













    • Have you tried to return a HTTP Status 204 with any address that requrests the URLs /generate_204 and /gen_204 ?

      – Rui F Ribeiro
      Mar 20 '18 at 21:48













    • I haven't tried it, but that's what Android is looking for as a confirmation that everything is fine, isn't it? That's what you do if you have a captive portal setup, but don't want android to know about it (to popup anything).

      – Tal
      Mar 21 '18 at 1:38











    • Hi I suppose when you resolved everything to one host it is remotely possible that nginx was actually giving the expected responses (204) hence no need for captive portal to pop up.

      – r0berts
      Aug 4 '18 at 19:01

















    Pretty interesting stuff. Still trying to think why.

    – Rui F Ribeiro
    Mar 20 '18 at 21:44







    Pretty interesting stuff. Still trying to think why.

    – Rui F Ribeiro
    Mar 20 '18 at 21:44















    Have you tried to return a HTTP Status 204 with any address that requrests the URLs /generate_204 and /gen_204 ?

    – Rui F Ribeiro
    Mar 20 '18 at 21:48







    Have you tried to return a HTTP Status 204 with any address that requrests the URLs /generate_204 and /gen_204 ?

    – Rui F Ribeiro
    Mar 20 '18 at 21:48















    I haven't tried it, but that's what Android is looking for as a confirmation that everything is fine, isn't it? That's what you do if you have a captive portal setup, but don't want android to know about it (to popup anything).

    – Tal
    Mar 21 '18 at 1:38





    I haven't tried it, but that's what Android is looking for as a confirmation that everything is fine, isn't it? That's what you do if you have a captive portal setup, but don't want android to know about it (to popup anything).

    – Tal
    Mar 21 '18 at 1:38













    Hi I suppose when you resolved everything to one host it is remotely possible that nginx was actually giving the expected responses (204) hence no need for captive portal to pop up.

    – r0berts
    Aug 4 '18 at 19:01





    Hi I suppose when you resolved everything to one host it is remotely possible that nginx was actually giving the expected responses (204) hence no need for captive portal to pop up.

    – r0berts
    Aug 4 '18 at 19:01













    0














    I spent a lot of time trying to figure this out and finally did it. You can see the code here https://github.com/tretos53/Captive-Portal



    It's a combination of domains pointing to public IPs, iptables and nginx redirects.






    share|improve this answer
























    • Hello and welcome to the U&L stack exchange site! Please review the Help Center to get information on how to best post to this site. To get to your answer, please consider editing it to add additional context. While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. Thank you!

      – kemotep
      Nov 27 '18 at 13:26


















    0














    I spent a lot of time trying to figure this out and finally did it. You can see the code here https://github.com/tretos53/Captive-Portal



    It's a combination of domains pointing to public IPs, iptables and nginx redirects.






    share|improve this answer
























    • Hello and welcome to the U&L stack exchange site! Please review the Help Center to get information on how to best post to this site. To get to your answer, please consider editing it to add additional context. While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. Thank you!

      – kemotep
      Nov 27 '18 at 13:26
















    0












    0








    0







    I spent a lot of time trying to figure this out and finally did it. You can see the code here https://github.com/tretos53/Captive-Portal



    It's a combination of domains pointing to public IPs, iptables and nginx redirects.






    share|improve this answer













    I spent a lot of time trying to figure this out and finally did it. You can see the code here https://github.com/tretos53/Captive-Portal



    It's a combination of domains pointing to public IPs, iptables and nginx redirects.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Nov 27 '18 at 10:06









    ag53ag53

    1




    1













    • Hello and welcome to the U&L stack exchange site! Please review the Help Center to get information on how to best post to this site. To get to your answer, please consider editing it to add additional context. While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. Thank you!

      – kemotep
      Nov 27 '18 at 13:26





















    • Hello and welcome to the U&L stack exchange site! Please review the Help Center to get information on how to best post to this site. To get to your answer, please consider editing it to add additional context. While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. Thank you!

      – kemotep
      Nov 27 '18 at 13:26



















    Hello and welcome to the U&L stack exchange site! Please review the Help Center to get information on how to best post to this site. To get to your answer, please consider editing it to add additional context. While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. Thank you!

    – kemotep
    Nov 27 '18 at 13:26







    Hello and welcome to the U&L stack exchange site! Please review the Help Center to get information on how to best post to this site. To get to your answer, please consider editing it to add additional context. While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. Thank you!

    – kemotep
    Nov 27 '18 at 13:26













    0














    There's a lot of different reasons for this, could be your specific hardware phone manufacturer as well. I currently built my Captive Portal library for Mongoose OS responding to all DNS queries with device IP and don't have any problems with devices besides Samsung devices which seem to require the 200 response with text.



    Here's the library stack:
    https://github.com/tripflex/captive-portal-wifi-stack



    Specifically here's the Captive Portal handling, along with details of the endpoints I use, and details in the README on how it's handled:



    https://github.com/tripflex/captive-portal



    You will see the one solution I used for Samsung devices is to return a 200 with a generated HTML files using a meta refresh tag:



    https://github.com/tripflex/captive-portal#cportalredirect_file-setting



    <html>
    <head>
    <title>Redirecting to Captive Portal</title>
    <meta http-equiv='refresh' content='0; url=PORTAL_URL'>
    </head>
    <body>
    <p>Please wait, refreshing. If page does not refresh, click <a href='PORTAL_URL'>here</a> to login.</p>
    </body>
    </html>


    But really the main thing was setting up endpoint handlings for each specific device:



    https://github.com/tripflex/captive-portal#known-endpoints





    • /mobile/status.php Android 8.0 (Samsung s9+)


    • /generate_204 Android


    • /gen_204 Android


    • /ncsi.txt Windows


    • /hotspot-detect.html iOS/OSX


    • /hotspotdetect.html iOS/OSX


    • /library/test/success.html iOS


    • /success.txt OSX


    • /kindle-wifi/wifiredirect.html Kindle when requested with com.android.captiveportallogin


    • /kindle-wifi/wifistub.html Kindle before requesting with captive portal login window (maybe for detection?)






    share|improve this answer




























      0














      There's a lot of different reasons for this, could be your specific hardware phone manufacturer as well. I currently built my Captive Portal library for Mongoose OS responding to all DNS queries with device IP and don't have any problems with devices besides Samsung devices which seem to require the 200 response with text.



      Here's the library stack:
      https://github.com/tripflex/captive-portal-wifi-stack



      Specifically here's the Captive Portal handling, along with details of the endpoints I use, and details in the README on how it's handled:



      https://github.com/tripflex/captive-portal



      You will see the one solution I used for Samsung devices is to return a 200 with a generated HTML files using a meta refresh tag:



      https://github.com/tripflex/captive-portal#cportalredirect_file-setting



      <html>
      <head>
      <title>Redirecting to Captive Portal</title>
      <meta http-equiv='refresh' content='0; url=PORTAL_URL'>
      </head>
      <body>
      <p>Please wait, refreshing. If page does not refresh, click <a href='PORTAL_URL'>here</a> to login.</p>
      </body>
      </html>


      But really the main thing was setting up endpoint handlings for each specific device:



      https://github.com/tripflex/captive-portal#known-endpoints





      • /mobile/status.php Android 8.0 (Samsung s9+)


      • /generate_204 Android


      • /gen_204 Android


      • /ncsi.txt Windows


      • /hotspot-detect.html iOS/OSX


      • /hotspotdetect.html iOS/OSX


      • /library/test/success.html iOS


      • /success.txt OSX


      • /kindle-wifi/wifiredirect.html Kindle when requested with com.android.captiveportallogin


      • /kindle-wifi/wifistub.html Kindle before requesting with captive portal login window (maybe for detection?)






      share|improve this answer


























        0












        0








        0







        There's a lot of different reasons for this, could be your specific hardware phone manufacturer as well. I currently built my Captive Portal library for Mongoose OS responding to all DNS queries with device IP and don't have any problems with devices besides Samsung devices which seem to require the 200 response with text.



        Here's the library stack:
        https://github.com/tripflex/captive-portal-wifi-stack



        Specifically here's the Captive Portal handling, along with details of the endpoints I use, and details in the README on how it's handled:



        https://github.com/tripflex/captive-portal



        You will see the one solution I used for Samsung devices is to return a 200 with a generated HTML files using a meta refresh tag:



        https://github.com/tripflex/captive-portal#cportalredirect_file-setting



        <html>
        <head>
        <title>Redirecting to Captive Portal</title>
        <meta http-equiv='refresh' content='0; url=PORTAL_URL'>
        </head>
        <body>
        <p>Please wait, refreshing. If page does not refresh, click <a href='PORTAL_URL'>here</a> to login.</p>
        </body>
        </html>


        But really the main thing was setting up endpoint handlings for each specific device:



        https://github.com/tripflex/captive-portal#known-endpoints





        • /mobile/status.php Android 8.0 (Samsung s9+)


        • /generate_204 Android


        • /gen_204 Android


        • /ncsi.txt Windows


        • /hotspot-detect.html iOS/OSX


        • /hotspotdetect.html iOS/OSX


        • /library/test/success.html iOS


        • /success.txt OSX


        • /kindle-wifi/wifiredirect.html Kindle when requested with com.android.captiveportallogin


        • /kindle-wifi/wifistub.html Kindle before requesting with captive portal login window (maybe for detection?)






        share|improve this answer













        There's a lot of different reasons for this, could be your specific hardware phone manufacturer as well. I currently built my Captive Portal library for Mongoose OS responding to all DNS queries with device IP and don't have any problems with devices besides Samsung devices which seem to require the 200 response with text.



        Here's the library stack:
        https://github.com/tripflex/captive-portal-wifi-stack



        Specifically here's the Captive Portal handling, along with details of the endpoints I use, and details in the README on how it's handled:



        https://github.com/tripflex/captive-portal



        You will see the one solution I used for Samsung devices is to return a 200 with a generated HTML files using a meta refresh tag:



        https://github.com/tripflex/captive-portal#cportalredirect_file-setting



        <html>
        <head>
        <title>Redirecting to Captive Portal</title>
        <meta http-equiv='refresh' content='0; url=PORTAL_URL'>
        </head>
        <body>
        <p>Please wait, refreshing. If page does not refresh, click <a href='PORTAL_URL'>here</a> to login.</p>
        </body>
        </html>


        But really the main thing was setting up endpoint handlings for each specific device:



        https://github.com/tripflex/captive-portal#known-endpoints





        • /mobile/status.php Android 8.0 (Samsung s9+)


        • /generate_204 Android


        • /gen_204 Android


        • /ncsi.txt Windows


        • /hotspot-detect.html iOS/OSX


        • /hotspotdetect.html iOS/OSX


        • /library/test/success.html iOS


        • /success.txt OSX


        • /kindle-wifi/wifiredirect.html Kindle when requested with com.android.captiveportallogin


        • /kindle-wifi/wifistub.html Kindle before requesting with captive portal login window (maybe for detection?)







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered 7 hours ago









        sMylessMyles

        1,408186




        1,408186






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Unix & Linux Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f432190%2fwhy-isnt-androids-captive-portal-detection-triggering-a-browser-window%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Loup dans la culture

            How to solve the problem of ntp “Unable to contact time server” from KDE?

            ASUS Zenbook UX433/UX333 — Configure Touchpad-embedded numpad on Linux