Iphone连接wifi热点跳转captive portal页面原理以及页面跳转慢原因分析

您所在的位置:网站首页 抓包303 Iphone连接wifi热点跳转captive portal页面原理以及页面跳转慢原因分析

Iphone连接wifi热点跳转captive portal页面原理以及页面跳转慢原因分析

2024-07-10 16:28| 来源: 网络整理| 查看: 265

Iphone连接wifi热点跳转captive portal页面原理以及页面跳转慢原因分析

May 19, 2017 | 20 Minute Read

iPhone里面有两种探测方式: 方法一:

猜测这个对应首次连接一个新的SSID,经过了文档提到的hotspothelper的“Evaluate”流程, 对应文档的“Sequence When Network Is Captive and UI Is Required” 流程。 不管有人说apple会使用了随机使用多个域名来探测。

第一个HTTP请求 captive.apole.com GET /hotspot-detect.html HTTP/1.0 User-Agent: CaptiveNetworkSupport-346.50.1 wispr\r\n Connection: close\r\n 第二个HTTP请求 www.apple.com GET / HTTP/1.1 Connection: keep-alive\r\n User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E304\r\n 第二步页面加载完之后,40毫秒后发送探测 www.apple.com GET /library/test/success.htm HTTP/1.0 User-Agent: CaptiveNetworkSupport-346.50.1 wispr Connection: close\r\n 用户提交数据后,连发两个第3步一样的探测包。 方法二:

猜测这个对应已经连过了的SSID,缓存直接找到best_helper 没有“Evaluate”阶段的流程, 对应文档的“Sequence When Network Is Captive (Cached)”,直接从Maintaining state 开始认证流程。

第一个HTTP请求 captive.apole.com GET /hotspot-detect.html HTTP/1.0 User-Agent: CaptiveNetworkSupport-346.50.1 wispr\r\n Connection: close\r\n 第二个HTTP请求 100毫秒秒后 captive.apole.com GET /hotspot-detect.html HTTP/1.1 Connection: keep-alive\r\n User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E304\r\n 等第2步的页面加载完后,过了200毫秒,又马上发送探测 captive.apole.com GET /hotspot-detect.html HTTP/1.0 User-Agent: CaptiveNetworkSupport-346.50.1 wispr\r\n Connection: close\r\n 第2步用户操作提交数据后,过了400毫秒, 马上连发2个和第3步一样的探测。 总结一下iPhone的认证页面跳转流程 +---------------------------------+ | “扫描附近wifi热点” scan 阶段 | 这个阶段iOS遍历所有的 helper,各个助手模块自己给wifi加注释( +-------------------+-------------+ 界面可以看到的wifi热点下面的描述吧)和提供wifi连接密码。 | 各个助手模块应该也可以强制设置某个wifi热点不需要认证,直接自动登录等。 | | 缓存里面是否有这个wifi热点的记录 | | +------------------------------->+ | | 否 | | 是 | | v v +-------------------------+ +-------------------------------+ | Evaluate阶段 | | Maintain阶段 | | CNA 发送 HTTP/1.0探测 | | | | Authenticate | | | +-------------------+------------------+ | | | PresentUI阶段 弹出认证窗口 | | | | | | | | CNA发送 HTTP/1.1 请求加载portal页面 | | | | 之后每次portal页面加载完成都会触发 | | | | HTTP/1.0探测 。如果HTTP/1.1连接超时 | | | | 会显示空白页,最后会报告服务器超时 | | | +---------------------+----------------+ | | | | 否 | +---------------------------------+ | | | ^ | HTTP/1.0 探测显示网络正常 | | | | v v | +------------+-------------+---------+ | | 认证完成,wifi “完成”按钮可用 | ------------------------------------------------>-+ +------------------------------------+ 其他观察结果和结论: iPhone 不会对 captive.apple.com或者www.apple.com GET /hotspot-detect.html HTTP/1.0 User-Agent: CaptiveNetworkSupport-346.50.1 wispr\r\n Connection: close\r\n

这个HTTP/1.0请求返回的的http response 进行任何处理,如果这个respone里面有各种跳转, iPhone也不会执行跳转。 页面跳转只会在 HTTP/1.1那个的结果里面进行。

跳转出来的Portal页面每次刷新,都会触发HTTP/1.0的探测包。 如果这个请求到了苹果的服务器 回复了Sucess页面的话(参考下面),iPhone就认为wifi网络正常,UI上面的 “完成”按钮就变为可用。 它这个探测发送的很快, 所以必须确保网关放行规则起作用后,页面还能刷新一次或者放行规则起 作用后页面再返回,触发这个探测来点亮图标。

利用http status code 302/303的跳转portal页面的方式,相比较javascript的跳转方式, 前者可以让iPhone少发一次HTTP/1.0探测包。

安装“wifi万能钥匙”的情况下,wifi万能钥匙和iphone自身都会发送探测包。 “wifi万能钥匙” 不但用iPhone还会用android的方式的HTTP来探测网络。 wifi万能钥匙发起的iphone探测 GET / HTTP/1.1\r\n Host: captive.apple.com\r\n User-Agent: Zeus/95 CFNetwork/811.4.18 Darwin/16.5.0\r\n Accept-Language: zh-cn\r\n Connection: keep-alive\r\n [Full request URI: http://captive.apple.com/] Apple服务器给它的回应 HTTP/1.1 304 Not Modified\r\n Cache-Control: max-age=300\r\n Connection: keep-alive\r\n Via: http/1.1 uslax1-edge-bx-005.ts.apple.com (ApacheTrafficServer/7.0.0)\r\n Server: ATS/7.0.0\r\n wifi万能钥匙发送的探测 GET /generate_204 HTTP/1.1\r\n Host: c.51y5.net\r\n Accept: */*\r\n Accept-Language: zh-cn\r\n Connection: keep-alive\r\n Accept-Encoding: gzip, deflate\r\n User-Agent: Zeus/95 CFNetwork/811.4.18 Darwin/16.5.0\r\n \r\n wifi万能钥匙自己服务器发送的andoird探测包回应 HTTP/1.1 204 No Content\r\n [Expert Info (Chat/Sequence): HTTP/1.1 204 No Content\r\n] Request Version: HTTP/1.1 Status Code: 204 Response Phrase: No Content Server: nginx\r\n Connection: keep-alive\r\n 正常iphone 发送的探测包 和回应 GET /hotspot-detect.html HTTP/1.0\r\n Host: captive.apple.com\r\n Connection: close\r\n User-Agent: CaptiveNetworkSupport-346.50.1 wispr\r\n 正常iPhone 服务器探测包的回应 HTTP/1.0 200 OK\r\n Cache-Control: max-age=300\r\n Accept-Ranges: bytes\r\n Content-Type: text/html\r\n Server: ATS/7.0.0\r\n CDNUUID: 8ec1d738-abf5-4482-ae50-d04bbd52e601-1473127713\r\n Via: https/1.1 jptyo5-edge-lx-010.ts.apple.com (ApacheTrafficServer/7.0.0), http/1.1 jptyo5-edge-bx-004.ts.apple.com (ApacheTrafficServer/7.0.0)\r\n X-Cache: hit-fresh, hit-fresh\r\n Etag: "41ba060eb1c0898e0a4a0cca36a8ca91"\r\n Age: 116\r\n \r\n SuccessSuccess\n

可以知道wifi万能钥匙的iPhone方式探测包url有点问题的。

如果某次页面弹出有问题,“完成” 按钮没有亮, 客人又点 “取消”,“继续使用网络”,那么 “auto-join”按钮会被禁用。 这个应该对应文档里面的 kNEHotspotHelperCommandTypePresentUI状态 返回“kNEHotspotHelperResultFailure (or any other result) A fatal error occurred; auto-join disabled.” 然后客人如果选择继续“使用网络”后。 在断开,重连到wifi热点来,因为auto-join被取消了,认证页面 也是不会再自动弹出来的。

Evaluate阶段导致的45超时问题 发现iPhone首次连新的ssid的热点时,弹出页面都是在45秒左右。(ios 10.3.1版本 安装有“wifi万能驱动”) 这个原因应该是首次连接时, iPhone默认执行“Evaluate”流程,给所有的系统所有的hotspothelper模块都发送 “Evaluate” 命令,系统部本身自带一个Captive Network Assitant(CNA) helper,安装了“Wifi万能驱动” “QQ浏览器wifi助手” 之后的APP之后,他们也都安装所有的Helper模块。 iOS系统发所有的模块下发命令后,要求各个模块回复“网络是否可用的评估值”。一旦有任何一个helper模块回复 “high” 表示这个模块很自信自己能够处理接下来的“登录认证”,那么其他没有返回结果的helper模块切换到后台,他们的结果 也会被忽略。如果所有的helper模块都没有任何回复,那么iOS直接认为网络可用,状态直接变成Authenticated“认证完成”。

而出现问题的原因,打开是各个wifi助手app没有实现这个Evaluate命令,或者由于某种原因一直不回复Evaluate命令。 系统默认的CNA helper的Evaluate被调用了使用 HTTP1.0 探测 captive.apple.com,但CNA helper了返回的结果应该是“Low” (不是很确定自己能不能处理后面的认证)。这样iOS还没看到一个“high”的结果,又有其他helper没有回复,就会一直等待 到45秒的超时结束。过了45秒之后才能勉强的把这个“low”结果的CNA作为best helper,在继续下面的 “Authenticating”。 这个其实是各个APP没有响应这个Evaluate命令造成的,如果助手APP快速返回“low”,iOS拿到所有helper的结果不会等待 这个45秒了。虽然这样处理iOS看到多个low的回复,只能任意选一个进行下面的“认证”了,认证的时候各个模块可以 再直接返回失败的让iOS把自己从helper列表删除,iOS会把不支持的helper 排除在外,然后再重做Evalute。 听说有厂商已经给这些“助手类hotspothelper”的开发者报告问题,大多的APP在最新版里面已经修复这个问题。 其实如果系统默认的CNA helper返回的是“high”也能避免这个超时等待,但对应该新的网络SSID,它确实不能保证后面能 不能认证成功返回low也是合理的。 如果自己实现APP helper模块,可以通过ssid和网络探测等方式知道是自己能处理的 wifi热点,直接返回high也是能够避免这个45秒的超市等待的。

但如果使用“CNA”成功做过一次portal认证之后,是没有这个45秒超时等待问题了。因为iOS有个cache缓存机制,第二次 连接的时候他通过cache知道这个“CNA” helper模块能够处理这个网络的认证,那么他就跳过“Evaluate”阶段,直接让 CNA helper开始“Maintaining”处理。所以第二次以后就没有这个“Evaluate”的45秒超时等待了。

根据apple开发者论坛的说法在iOS的任务管理器里面把“wifi助手”进程给结束掉,iOS就也不会调用这些APP的hotspothelper 这样没有其他app hotspothelper的干扰,应该也可以避免这个首次连接的超时问题。

这个HotspotHelper接口按apple的说法是专门给用了开发“captive portal”的app用的,专门用来辅助wifi热点认证的。 hotspothelper模块在整个认证过程中出现错误,可能导致iOS把这个模块从这个wifi网络的“有效列表”中删除,不再参与后面的 重试。可能 wifi热点还会被 “断开” ,或者wifi热点的“auto-join”按钮被禁用等。这都可能影响到最终的认证行为。具体可以 参考Apple的状态说明。

其他导致页面显示慢的问题,估计主要是网络原因导致 HTTP/1.0和 HTTP/1.1两个连接创建失败或者超时导致的 如果到了“PresentUI阶段”,但HTTP1.1的请求portal页面的请求失败了,那么就显示的是空白页面了。其他的影响 因素还有dns解析的速度,网关或者防火墙是否有连接数量限制导致的丢包等等。 可以pc连接上网络,然后暂时不认证portal页面,然后用一些http测试工具测试一下portal服务器连接的性能,抓包 看看,比如用这个工具https://github.com/rakyll/hey ./hey -more=1 -n 256 -c 4 -q 4 http://captive.apple.com/hotspot-detect.htm

另外需要避免portal页面的http服务打开了TCP的tcp_tw_recycle选项,然后用户全部通过NAT网络连接这个服务器。 这样tcp的timestamp选项和tcp_tw_recycle一起使用时, NAT网络用户的创建连接会很大概率失败。 原因这两篇文章有介绍: 一个NAT问题引起的思考 http://perthcharles.github.io/2015/08/27/timestamp-NAT/ Coping with the TCP TIME-WAIT state on busy Linux servers https://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux

按照苹果文档的说法,wifi热点还没有认证通过的时候(“非Authenticated”),默认 路由是不会是wifi设备的,自己的helper里面需要从wifi上面发送探测网络包,是需要 明确指定了出去的网络设备才行的。但在实际测试发现,不知道是不是一些助手helper 模块的干扰,有的时候是会看到一些 “支付宝”或者“微信”等的443端口的https还有8080 或者80的端口出来。有可能这是wifi被错误的设置了 authenticated 状态,又要等到 后面CNA探测到问题再开始显示portal页面。但在这之前已经有很多后台页面尝试在wifi 设备上发送请求了,有的比如qq的浏览器加载portal页面,还会自己尝试加载重定向后 的页面等等。

测试方法

笔记本windows 10系统 + “移动热点” + 手机无线连笔记本 + dns劫持+ wireshark抓包

配置测试环境 右键 开始菜单 -> “设置“ -> “网络设置” -> “移动热点” 在网络设备里面找到移动热点对应的 网络设备,修改 dns服务器地址为本地虚假dns服务器的ip。 这个ip 不要用和 改设备一样的,使用本机的其他ip比如承载网卡的ip。 保证 手机端获取的dns转到自己的dns服务器就可以了。 用于测试golang dns server 代码。

package main import ( "flag" "fmt" "github.com/miekg/dns" "html/template" "log" "math/rand" "net" "net/http" "os" "regexp" "strings" "sync" "time" ) const ( DEFAULT_PORT = 53 DEFAULT_TIMEOUT = 20 * time.Second DEFAULT_PROTO = "udp" ) var ( LOG *log.Logger FAKE_IP net.IP PROTO string TIMEOUT time.Duration DEBUG bool TCP_REGEX *regexp.Regexp DEFAULT_SERVERS = []string{ // "8.8.8.8", // google // "8.8.4.4", // google // "208.67.222.222", // OpenDNS // "208.67.220.220", // OpenDNS "114.114.114.114", // 114dns "180.76.76.76", // baidu "223.5.5.5", // ali "223.6.6.6", // ali "119.29.29.29", // tencent dnspod } LOGIN_USER = make([]int64, 0, 8) USER_MUTEX = &sync.RWMutex{} REDIRECT_MODE int ) func ipv4ToInt64(ip net.IP) int64 { if DEBUG { fmt.Println("ip to int64: ", ip) } ip4 := ip.To4() if len(ip4) == net.IPv4len { n := (int64(ip4[0]) 0 { i := rand.Intn(sl) s := p.Servers[i] if strings.Index(s, ":") == -1 { s = fmt.Sprintf("%s:%d", s, DEFAULT_PORT) } return s } return "8.8.8.8:53" } // ---------------------------------------------------------------------------- type PortalServer struct{} func (h *PortalServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { var urlPath = r.URL.Path fmt.Println("query url:", urlPath) if urlPath == "/login.go" { if len(r.FormValue("UserName")) != 0 { if len(r.FormValue("WISPrVersion")) != 0 || len(r.FormValue("FNAME")) != 0 || len(r.FormValue("OriginatingServer")) != 0 { fmt.Println("用户使用wispr方式登录") } fmt.Fprint(w, "Success"+r.FormValue("id")+" 登录成功"+" ") ip, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { fmt.Println("Failed to SplitHostPort:", r.RemoteAddr) return } userIP := net.ParseIP(ip) if userIP == nil { fmt.Println("Failed to ParseIP:", ip) return } addUser(ipv4ToInt64(userIP)) // 页面返回后,iPhone很快就下发探测包。我们慢点回复确保它探测的时候AddUser // 规则已经其作用了。 time.Sleep(500 * time.Millisecond) if DEBUG { fmt.Println("user has submit id to login.go:", ip) } } else { fmt.Fprint(w, ` portal 用户名: 密码: `) } return } switch REDIRECT_MODE { default: // case 0: // 直接输出 fmt.Fprint(w, ` portal 用户名: 密码: `) case 1: // http/1.0 重定向 // overwrite the client http version r.Proto = "HTTP/1.0" r.ProtoMajor = 1 // r.ProtoMinor = 0 if r.ProtoMinor == 0 { http.Redirect(w, r, "/login.go?mode=11", http.StatusSeeOther) } else { http.Redirect(w, r, "/login.go?mode=12", http.StatusSeeOther) } case 2: // http/1.1 重定向 // overwrite the client http version r.Proto = "HTTP/1.1" r.ProtoMajor = 1 r.ProtoMinor = 1 http.Redirect(w, r, "/login.go?mode=2", http.StatusSeeOther) case 3: // javescript 跳转 portalUrl := "http://" + FAKE_IP.To4().String() + "/login.go?mode=3" tmpl, err := template.New("foo").Parse( ` location.replace("");

Welcome. If the browser cannot be redirected automatically, please click here, Thank you. `) // type TemplateData struct { // PortalUrl string // } if err != nil { fmt.Println("Template parse error. ", err) fmt.Fprint(w, "Template parse error.") return } err = tmpl.Execute(w, struct{ PortalUrl string }{PortalUrl: portalUrl}) if err != nil { fmt.Println("Template execute error. ", err) fmt.Fprint(w, "Template execute error.") } case 4: // wispr // wispr specification 在网上已经找不到了。 // 参考 https://github.com/wichert/wispr/blob/master/src/wispr/__init__.py 这里的代码大概猜测一下 // https://docs.microsoft.com/en-us/windows-hardware/drivers/mobilebroadband/wispr-authentication wisprLoginUrl := "http://" + FAKE_IP.To4().String() + "/login.go" // 临时把method改一下,不然method == "GET" 的时候Redirect方法写一条链接进去网页内容里面去,我们不要写这个 method := r.Method r.Method = "POST" http.Redirect(w, r, wisprLoginUrl, http.StatusFound) r.Method = method fmt.Fprint(w, ` ` case 5: // http head refresh 重定向 非标准,但大多浏览器都支持 // req.Header.Add("If-None-Match", `W/"wyzzy"`) portalUrl := "http://" + FAKE_IP.To4().String() + "/login.go?mode=5" // w.Header().Set("Refresh", "0; url="+portalUrl) // android 不支持这个,但支持下面的。IE 两个都支持 fmt.Fprint(w, `portal`) } } func PrintLocalIP() { ifaces, err := net.Interfaces() // handle err if err != nil { fmt.Println("获取不到本地ip地址\n") return } for _, i := range ifaces { addrs, err := i.Addrs() if err != nil { fmt.Println("获取不到本地ip地址\n") return } for _, address := range addrs { // check the address type and if it is not a loopback the display it if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { if ipnet.IP.To4() != nil { fmt.Println(`可以通过这个地址访问 http:\\` + ipnet.IP.String()) } } } } } //------------------------------------------------------------------------------ func main() { go func(msg string) { var h PortalServer // http.ListenAndServe("localhost:4000", &h) fmt.Println("\nhttp portal服务器已经启动") PrintLocalIP() http.ListenAndServe(":80", &h) }("http go coroutine") // ---------------------- var servers []string args := flag.Args() if len(args) > 0 { servers = append(servers, args...) } else { servers = DEFAULT_SERVERS } LOG.Printf("Servers: %s", servers) proxyer := Proxy{servers} if err := dns.ListenAndServe("0.0.0.0:53", "udp", proxyer); err != nil { LOG.Fatal(err) } }

这里http server和dns都写到一起了,这里只是让dns返回本地http的ip就可以,认证之前 所有的http都被域名劫持跳转到本地了。 测试android和iPhone都可以连wifi后自动正常跳出portal认证页面。

其他有用的测试手段

通过Xcode 或者iTools工具查看iPhone的syslog (console log) 测试之前,先安装iTools 4.0 (http://www.itools.cn/) 然后iPhone 通过数据线和电脑连接,iTools里面选择 “实时日志”。 然后再进行测试, 根据网上说法,登陆认证是CNA如果出错会有syslog记录。

抓取iPhone的网络通讯包 iOS Packet Tracing(利用usb链接iPhone和MAC电脑,然后使用rvictl在pc可以模拟出一个网络设备抓iPhone的网络包) https://developer.apple.com/library/content/qa/qa1176/_index.html#//apple_ref/doc/uid/DTS10001707-CH1-SECIOSPACKETTRACING

上Apple develop 论坛( Apple Developer Forums / Core OS / Networking) 有个Apple的技术工程师叫做eskimo的好像对NEhotspothelper接口很了解, 他留有邮箱可以向他咨询吧

参考文档:

Apple官方文档的“Hotspot Network Subsystem Programming Guide”里面的wifi认证状态机的说明 https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/Hotspot_Network_Subsystem_Guide/Contents/AuthStateMachine.html#//apple_ref/doc/uid/TP40016639-CH2-SW1

Apple的 Network Extenstion接口 https://developer.apple.com/reference/networkextension

https://forums.developer.apple.com/message/205571#205571 https://forums.developer.apple.com/message/223136#223136

2017-06-22补充

通过测试和手机查看iPhone手机的syslog,evaluate阶段系统内置插件(BUILTIN)返回的是 confidence none。 另外像“微信”这样的应用,如果没有开移动蜂窝网络登录微信,也会evaluate阶段超时,等待45秒才能弹出portal页面。 应该是微信里面的public wifi功能也实现了这个接口,但在没有网络时没有正确处理这个evaluate命令。给微信的人反馈 了就不知道他们后续怎么处理了。

从测试来看,iPhone的portal页面慢,这个evaluate超时是出现较多的,很多应用,什么“地铁wifi”类似的wifi助手类应用 都可能有影响,连微信都有问题。那肯定要怪苹果文档没写好吧,各个应用开发者都没有实现对。

另外也看到 iPhone的springboard(桌面管理进程?) 出现崩溃,导致CaptiveNetworkSupport发送launch Websheet时候 启动com.apple.WebSheet应用失败,这样最终10秒超时后日志记录websheet died报告,iPhone在PresentUI阶段返回临时 错误,就会临时断开wifi热点。因为websheet是专门显示portal页面的进程,启动不起来,那肯定就有问题了。iPhone这个 时候应该会进入死循环,不停在各个ssid直接做wifi漫游。但都会由于这个问题连不上。出现这种情形应该比较少见,但一旦出现 只能通过放行流量,让苹果在evaluate阶段就探测认为是no captive网络,这样就需要弹portal网页直接可以使用wifi流量, 因为后面的PresentUI阶段肯定是会失败。这个应该是iPhone自身的问题,只能通过重启系统来让各个模块恢复正常了。 苹果笔记本上面也观察到类似现象。

如果网络被评估为no captive 网络,auto-join和auto-login按钮不可设置,不可用。反之这两个选项才可以设置。 各个认证阶段失败,ios禁用或者永久禁用这个wifi热点的,参考苹果的文档。

iOS只会在判断wifi网络可用后,才会把默认路由切换到wifi接口上面来。 但如果cache里面 有提示wifi 是no captive 的也会马上切换过来,这个可能会有些影响,因为到真正检测到网络是不是可用还需要探测。那些探测(probe)网络是否可用 的HTTP包发送时应该是需要专门绑定的wifi的interface才会从这个非默认路由的接口出来吧。

苹果这个capitve portal页面有3个模块,后面文章的里面我有详细分析各个模块的功能。各个进程之间通过xpc来进行跨进程通讯。

CaptiveNetworkSupport: 运行在configd进程里面,hotspot的状态机处理主要逻辑都在这里面。会在适当的时候通知其他两个。 captiveagent: 独立的进程,专门发送HTTP/1.0探测。 WebSheet: 独立进程,专门显示HTTP/1.1 供用户操作的UI,由captiveNetworkSupport唤醒 2017-06-23补充

按照文档,有一个300秒的Maintaining timer.,这样每隔300秒ios都会主动发送一个 http请求去探测。 但根据实际测试,抓包和syslog都没有看到这个间隔300秒的探测包。 大概看了一下代码,日志里面有“Enabling passive detection”,实在PassiveDetectSetNotificationCallBack 函数里面打印出来的, 它监控 “com.apple.symptoms.managed_events.captive-network” 这个notification事件,收到事件之后再做maintain探测。 启用了这个之后CNInfoAuthenticated就没有设置这个300秒的定时器事件了。 这个不知道事件不知道怎么触发的,反正re-join同一个 wifi热点肯定是触发maintain的探测的。 这个是ios 10.2的测试结果。但ios 10.3.2 里面又没有这个“Enabling passive detection” 了,不知道怎么回事,反正确实没有看到这个300秒的探测。

2017-07-10 补充

苹果锁屏后屏幕关闭后,如果手机没有一直充电,wifi会自动断开。等重新点亮屏幕之后才重新链接wifi。 解锁后,苹果会马上发送HTTP/1.0探测,如果页面正常返回Success页面不需要验证的情况,手机状态栏 会立即显示wifi图标,wifi网络应该马上接通。反之如果HTTP/1.0的probe检测到captive portal 页面,那就是需要 弹出UI验证页面。CaptiveNetworkSupport需要显示页面之前会有个UIAllowed()的检查,这个检查按照字幕 意思应该是在屏幕点亮解锁之后可以显示UI的时候才通过。实际测试发现,只有用户操作打开浏览器访问网络 了,或者用户打开系统的“wifi设置”,这个UIAllowed()的检查才通过,手机才会通知websheet应用加载HTTP/1.1 页面弹出验证页面。 如果用户没有操作,估计没有网络流量访问的时候,是不会自动弹出websheet登录验证窗口的。 从syslog和反汇编的代码看, CaptiveNetworkSupport 打印“en0 waiting for UI” 的log之后就一直等待系统发送 “com.apple.airport.userNotification“ ”com.apple.airportsettingsvisible“ 的这两个notification的某一个过来时候才会进行 ”launch websheet“的动作。按照苹果的文档前面一个notification应该对应”network state changes“ 网络状态变化通知, 后面对应系统”设置wifi“ 界面显示的通知。所以前面写的用户的操作触发了弹出 portal页面是很合理的。 这个苹果的实现估计可以做的更好一些,一般人都是希望马上弹出验证框,然后马上连上wifi的。但ios现在的实现 依赖这两个通知,只有浏览器之类网络访问或者”wifi设置“显示了的动作才触发系统下发这个通知,虽然没有太大问题, 但会觉得wifi不会马上自动连接是个问题。

« 前一篇:  Linux内核ipset源码阅读 后一篇: Windows10系统修改tcp的timestamp选项 »


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3