ionic start ionic-react-netscan blank --type=react
cd ionic-react-netscan
npm install @capacitor/core @capacitor/cli
npx cap init
Cd ..
mkdir netscan
cd netscan
go mod init netscan
go get golang.org/x/mobile/cmd/gomobile
gomobile bind -target=ios,android ./netscan
npm init @capacitor/plugin
npx ionic build
-> edit
/Users/mohammedgabr/ubuntu/shared/my_code/ionic-go-scanner-2/ionic-
react-netscan/netscan/android/src/main/java/com/portablesecuredapp/
plugin/NetscanPlugin.java
cap sync android
npx cap run android
// Go code (netscan.go)
package netscan
import (
"C"
"encoding/json"
"fmt"
"net/http"
"strings"
"sync"
"time"
"golang.org/x/net/html"
)
type ScanResult struct {
IP string `json:"ip"`
Port string `json:"port"`
Title string `json:"title"`
}
//export Scan
func Scan(ipRange *C.char, ports *C.char) *C.char {
ips := strings.Split(C.GoString(ipRange), "-")
portList := strings.Split(C.GoString(ports), ",")
var results []ScanResult
var wg sync.WaitGroup
resultChan := make(chan ScanResult)
for _, ip := range ips {
for _, port := range portList {
wg.Add(1)
go func(ip, port string) {
defer wg.Done()
url := fmt.Sprintf("http://%s:%s", ip, port)
title := getTitle(url)
if title != "" {
resultChan <- ScanResult{IP: ip, Port: port,
Title: title}
}
}(ip, port)
}
}
go func() {
wg.Wait()
close(resultChan)
}()
for result := range resultChan {
results = append(results, result)
}
jsonResults, _ := json.Marshal(results)
return C.CString(string(jsonResults))
}
func getTitle(url string) string {
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Get(url)
if err != nil {
return ""
}
defer resp.Body.Close()
tokenizer := html.NewTokenizer(resp.Body)
for {
tokenType := tokenizer.Next()
switch tokenType {
case html.ErrorToken:
return ""
case html.StartTagToken, html.SelfClosingTagToken:
token := tokenizer.Token()
if token.Data == "title" {
tokenType = tokenizer.Next()
if tokenType == html.TextToken {
return tokenizer.Token().Data
}
}
}
}
}
// src/capacitor-go-plugin/src/definitions.ts
export interface NetScanPlugin {
scan(options: { ipRange: string; ports: string }):
Promise<ScanResult[]>;
}
export interface ScanResult {
ip: string;
port: string;
title: string;
}
// src/pages/Home.tsx
import React, { useState } from 'react';
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar,
IonButton, IonInput, IonItem, IonLabel } from '@ionic/react';
import { Plugins } from '@capacitor/core';
import ScanResults from '../components/ScanResults';
const { NetScan } = Plugins;
const Home: React.FC = () => {
const [ipRange, setIpRange] = useState('');
const [ports, setPorts] = useState('');
const [scanResults, setScanResults] =
useState<ScanResult[]>([]);
const handleScan = async () => {
try {
const results = await NetScan.scan({ ipRange, ports });
setScanResults(results);
} catch (error) {
console.error('Scan failed:', error);
}
};
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>HTTP Scanner</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent className="ion-padding">
<IonItem>
<IonLabel position="floating">IP Range (e.g.,
192.168.1.1-192.168.1.10)</IonLabel>
<IonInput value={ipRange} onIonChange={e =>
setIpRange(e.detail.value!)} />
</IonItem>
<IonItem>
<IonLabel position="floating">Ports (comma-
separated, e.g., 80,443,8080)</IonLabel>
<IonInput value={ports} onIonChange={e =>
setPorts(e.detail.value!)} />
</IonItem>
<IonButton expand="block"
onClick={handleScan}>Start Scan</IonButton>
<ScanResults results={scanResults} />
</IonContent>
</IonPage>
);
};
export default Home;
// src/components/ScanResults.tsx
import React from 'react';
import { IonList, IonItem, IonLabel } from '@ionic/react';
import { ScanResult } from
'../capacitor-go-plugin/src/definitions';
interface ScanResultsProps {
results: ScanResult[];
}
const ScanResults: React.FC<ScanResultsProps> =
({ results }) => {
return (
<IonList>
{results.map((result, index) => (
<IonItem key={index}>
<IonLabel>
<h2>{result.ip}:{result.port}</h2>
<p>{result.title}</p>
</IonLabel>
</IonItem>
))}
</IonList>
);
};
export default ScanResults;
——
// netscan/netscan.go
package netscan
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"sync"
"time"
"golang.org/x/net/html"
)
type ScanResult struct {
IP string `json:"ip"`
Port string `json:"port"`
Title string `json:"title"`
}
// Netscan is an exported struct to hold our methods
type Netscan struct{}
// Scan is an exported method of Netscan
func (n *Netscan) Scan(ipRange string, ports string) string {
ips := strings.Split(ipRange, "-")
portList := strings.Split(ports, ",")
var results []ScanResult
var wg sync.WaitGroup
resultChan := make(chan ScanResult)
for _, ip := range ips {
for _, port := range portList {
wg.Add(1)
go func(ip, port string) {
defer wg.Done()
url := fmt.Sprintf("http://%s:%s", ip, port)
title := getTitle(url)
if title != "" {
resultChan <- ScanResult{IP: ip, Port: port,
Title: title}
}
}(ip, port)
}
}
go func() {
wg.Wait()
close(resultChan)
}()
for result := range resultChan {
results = append(results, result)
}
jsonResults, _ := json.Marshal(results)
return string(jsonResults)
}
func getTitle(url string) string {
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Get(url)
if err != nil {
return ""
}
defer resp.Body.Close()
tokenizer := html.NewTokenizer(resp.Body)
for {
tokenType := tokenizer.Next()
switch tokenType {
case html.ErrorToken:
return ""
case html.StartTagToken, html.SelfClosingTagToken:
token := tokenizer.Token()
if token.Data == "title" {
tokenType = tokenizer.Next()
if tokenType == html.TextToken {
return tokenizer.Token().Data
}
}
}
}
}
// NewNetscan creates a new instance of Netscan
func NewNetscan() *Netscan {
return &Netscan{}
}
——
—home.tsximport React, { useState } from 'react';
import { IonContent, IonHeader, IonPage, IonTitle,
IonToolbar, IonButton, IonInput, IonItem, IonLabel } from
'@ionic/react';
import { Plugins } from '@capacitor/core';
import ScanResults from '../components/ScanResults';
const { NetScan } = Plugins;
const Home: React.FC = () => {
const [ipRange, setIpRange] = useState('');
const [ports, setPorts] = useState('');
const [scanResults, setScanResults] =
useState<any[]>([]);
const handleScan = async () => {
console.log('Scan button clicked');
try {
console.log('Calling NetScan.scan with:', { ipRange,
ports });
const results = await NetScan.scan({ ipRange,
ports });
console.log('Scan results:', results);
setScanResults(results);
} catch (error) {
console.error('Scan failed:', error);
alert('Scan failed: ' + (error as Error).message);
}
};
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Network Scanner</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent className="ion-padding">
<IonItem>
<IonLabel position="floating">IP Range (e.g.,
192.168.1.1-192.168.1.10)</IonLabel>
<IonInput value={ipRange} onIonChange={e =>
setIpRange(e.detail.value!)} />
</IonItem>
<IonItem>
<IonLabel position="floating">Ports (comma-
separated, e.g., 80,443,8080)</IonLabel>
<IonInput value={ports} onIonChange={e =>
setPorts(e.detail.value!)} />
</IonItem>
<IonButton expand="block"
onClick={handleScan}>Start Scan</IonButton>
<ScanResults results={scanResults} />
</IonContent>
</IonPage>
);
};
export default Home;
—
ScanResults.tsx
// src/components/ScanResults.tsx
import React from 'react';
import { IonList, IonItem, IonLabel } from '@ionic/react';
import { ScanResult } from
'../capacitor-go-plugin/src/definitions';
interface ScanResultsProps {
results: ScanResult[];
}
const ScanResults: React.FC<ScanResultsProps> =
({ results }) => {
return (
<IonList>
{results.map((result, index) => (
<IonItem key={index}>
<IonLabel>
<h2>{result.ip}:{result.port}</h2>
<p>{result.title}</p>
</IonLabel>
</IonItem>
))}
</IonList>
);
};
export default ScanResults;