1import { useEffect, useRef, useState } from "react"
2import { Toaster, toast } from "react-hot-toast"
3import { network, encryption, password, notifyStyle, handshake } from "./lib/config"
4import NavSlider from "./components/ui/nav-slider"
5import NetworkScreen from "./screens/NetworkScreen"
6import { Button } from "./components/ui/button"
7import SetupScreen from "./screens/SetupScreen"
8import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from "./components/ui/sheet"
9import LogsScreen from "./screens/LogsScreen"
10
11function App() {
12 const [networks, setNetworks] = useState<network[] | null>(null)
13 const [networksWithHandshakes, setnetworksWithHandshakes] = useState<network[]>([])
14 const [section, setSection] = useState<number>(0)
15 const [logs, setLogs] = useState<string[]>([])
16 const ws = useRef<WebSocket | null>(null)
17 const connected = useRef<boolean>(false)
18 const initialConnection = useRef<boolean>(true)
19
20 async function retryConnection() {
21 let style: any = notifyStyle
22 style.style["textAlign"] = "center"
23 try {
24 ws.current = new WebSocket("ws://172.0.0.1:81/attack")
25 ws.current.onopen = () => {
26 connected.current = true
27 initialConnection.current = false
28 toast.dismiss()
29 toast.success("Connected to device")
30 setNetworks(null)
31 setSection(0)
32 fetchData()
33 ws.current?.send("ping")
34 }
35
36 ws.current.onmessage = (event) => {
37 if (event.data === "Victim connected") {
38 setLogs((prev) => [...prev,"Victim connected"]);
39 return toast.success("Victim connected")
40 }
41 if (event.data === "Victim disconnected") {
42 setLogs((prev) => [...prev, "Victim disconnected"]);
43 return toast.error("Victim disconnected")
44 }
45 const data = JSON.parse(event.data)
46 if (data) {
47 if (data.type === "password") {
48 setLogs((prev) => [...prev, "Victim entered password : " + data.password]);
49 return toast.success("Victim entered password");
50 }
51 if (data.type === "message") {
52 setLogs((prev) => [...prev, data.message]);
53 return
54 }
55 setLogs((prev) => {
56 return [...prev, data];
57 });
58 }
59 }
60 ws.current.onclose = () => {
61 if (initialConnection.current) {
62 toast.loading("Could not connect to device", style)
63 initialConnection.current = false
64 } else if (connected.current) {
65 toast.error("Connection to device lost", style)
66 connected.current = false
67 initialConnection.current = true
68 setNetworks(null)
69 setSection(0)
70 }
71 retryConnection()
72 }
73
74 setTimeout(() => {
75 if (ws.current?.readyState !== 1) {
76 ws.current?.close()
77 }
78 }, 15000)
79 } catch (err) {
80 console.log(err, "WebSocket error")
81 }
82 }
83
84 const checkHandshake = (datas: handshake[], bssid: string): boolean => {
85 for (let i in datas) {
86 let incoming = bssid.replaceAll(":", "").toLowerCase()
87 if (incoming === datas[i].dstAddress) {
88 return true
89 }
90 }
91 return false
92 }
93 const checkPassword = (datas: any, ssid: string): password[] => {
94 let passwords: password[] = []
95 for (let i in datas) {
96 if (ssid === datas[i].ssid) {
97 let valid = false
98 if (datas[i].handshakes && datas[i].matched) {
99 valid = true
100 }
101 passwords.push({
102 password: datas[i].password,
103 valid: valid,
104 })
105 }
106 }
107 return passwords
108 }
109
110 const fetchData = async () => {
111 const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
112 await sleep(1000)
113 const get_networks = await fetch("http://172.0.0.1/get_networks")
114 const get_datas = await fetch("http://172.0.0.1/get_datas")
115 // const get_networks = await fetch("/temp/temp-networks.json")
116 // const get_datas = await fetch("/temp/temp-datas.json")
117 const data = await get_networks.json()
118 const datas = await get_datas.json()
119 if (data.length === 0) {
120 console.log("No networks found")
121 setNetworks([])
122 setnetworksWithHandshakes([])
123 }
124 for (let i in data) {
125 let new_network: network = {
126 ssid: data[i].ssid,
127 bssid: data[i].bssid,
128 signal: parseInt(data[i].signal),
129 channel: parseInt(data[i].channel),
130 encryption: data[i].encryption,
131 deauthState: data[i].deauthState === "1" ? true : false,
132 handshake: checkHandshake(datas.handshakes, data[i].bssid),
133 password: checkPassword(datas.passwords, data[i].ssid),
134 handshakeData: datas.handshakes.filter(
135 (handshake: handshake) =>
136 handshake.dstAddress === data[i].bssid.replaceAll(":", "").toLowerCase()
137 ),
138 }
139 setNetworks((prev) => {
140 return prev ? [...prev, new_network] : [new_network]
141 })
142 }
143 for (let i in datas.handshakes) {
144 let newNetwork: network = {
145 ssid: datas.handshakes[i].ssid,
146 bssid: datas.handshakes[i].dstAddress,
147 signal: 0,
148 channel: 0,
149 encryption: encryption.UNKNOWN,
150 deauthState: false,
151 handshake: true,
152 password: checkPassword(datas.passwords, datas.handshakes[i].ssid),
153 handshakeData: [datas.handshakes[i]],
154 }
155 setnetworksWithHandshakes((prev) => {
156 if (prev) {
157 let found = prev.find((network) => network.bssid === newNetwork.bssid)
158 if (found) {
159 return prev
160 }
161 }
162 return prev ? [...prev, newNetwork] : [newNetwork]
163 })
164 }
165 }
166 useEffect(() => {
167 retryConnection()
168 document.title = "Portal-Xtend"
169 }, [])
170
171 return (
172 <div className="flex flex-col h-screen bg-[#040a1b] justify-center items-center overflow-hidden">
173 <h1 className="text-[#77AADD] font-semibold text-[50px] mb-2">Portal-Xtend</h1>
174 <Toaster position="bottom-center" />
175 <div className="flex flex-col justify-center items-center">
176 {networks != null ? (
177 <>
178 <NavSlider
179 activeNav={section}
180 setActiveNav={setSection}
181 children={["Networks", "Setup","Logs"]}
182 />
183 {section === 0 ? (
184 <>
185 <NetworkScreen networks={networks} />
186 <Button
187 variant="link"
188 onClick={() => {
189 setNetworks(null)
190 setSection(0)
191 fetchData()
192 }}
193 >
194 Refresh
195 </Button>
196 </>
197 ) : section === 1 ? (
198 <SetupScreen networks={networksWithHandshakes} />
199 ) :
200 <LogsScreen logs={logs} setLogs={setLogs}/>
201 }
202 </>
203 ) : (
204 <div className="loading">
205 <svg width="64px" height="48px">
206 <polyline
207 points="0.157 23.954, 14 23.954, 21.843 48, 43 0, 50 24, 64 24"
208 id="back"
209 ></polyline>
210 <polyline
211 points="0.157 23.954, 14 23.954, 21.843 48, 43 0, 50 24, 64 24"
212 id="front"
213 ></polyline>
214 </svg>
215 </div>
216 )}
217 </div>
218 </div>
219 )
220}
221
222export default App