Skip to content

Commit

Permalink
feat: allow use range of port
Browse files Browse the repository at this point in the history
  • Loading branch information
muzea committed Aug 15, 2019
1 parent e10f2bd commit 693ab64
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 99 deletions.
145 changes: 82 additions & 63 deletions portfw.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import (
"net"
"net/http"
"strconv"
"strings"

"github.com/docker/go-connections/proxy"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)

var portSep = "/"

var tcpProxyPool map[int]*proxy.TCPProxy
var udpProxyPool map[int]*proxy.UDPProxy

Expand All @@ -32,74 +35,107 @@ func getFileContent(fileName string) []byte {
return buf
}

func addProxyItem(localPort string, target string) {
targetTCPAddr, err := net.ResolveTCPAddr("tcp", target)
if err != nil {
log.Fatalln(err)
}
var iLocalPortStart int
var iLocalPortEnd int
if strings.Index(localPort, portSep) >= 0 {
_, err := fmt.Sscanf(localPort, "%d/%d", &iLocalPortStart, &iLocalPortEnd)
if err != nil {
log.Fatalln(err)
}
} else {
iLocalPortStart, err = strconv.Atoi(localPort)
if err != nil {
log.Fatalln(err)
}
iLocalPortEnd = iLocalPortStart
}
for i := 0; (iLocalPortStart + i) <= iLocalPortEnd; i++ {
targetTCPAddrCurrent := &net.TCPAddr{IP: targetTCPAddr.IP, Port: targetTCPAddr.Port + i, Zone: targetTCPAddr.Zone}
targetUDPAddrCurrent := &net.UDPAddr{IP: targetTCPAddr.IP, Port: targetTCPAddr.Port + i, Zone: targetTCPAddr.Zone}
go prepareTCPHandler(iLocalPortStart+i, targetTCPAddrCurrent)
go prepareUDPHandler(iLocalPortStart+i, targetUDPAddrCurrent)
}
configResult.Proxy[localPort] = target
log.Printf("proxy %s to %s", localPort, target)
}

func resolveConfig() {
configContent := getFileContent("./config.json")
json.Unmarshal(configContent, &configResult)
for localPort, target := range configResult.Proxy {
go prepareTCPHandler(localPort, target)
go prepareUDPHandler(localPort, target)
log.Printf("proxy %s to %s", localPort, target)
addProxyItem(localPort, target)
}
}

func prepareTCPHandler(localPort string, target string) {
local := fmt.Sprintf(":%s", localPort)
func prepareTCPHandler(localPort int, targetAddr *net.TCPAddr) {
local := fmt.Sprintf(":%d", localPort)
localAddr, err := net.ResolveTCPAddr("tcp", local)
if err != nil {
log.Fatalln(err)
}
incomingPort, err := strconv.Atoi(localPort)
if err != nil {
log.Fatalln(err)
}
targetAddr, err := net.ResolveTCPAddr("tcp", target)
if err != nil {
log.Fatalln(err)
}
localProxy, err := proxy.NewTCPProxy(localAddr, targetAddr)
if err != nil {
log.Fatalln(err)
}
tcpProxyPool[incomingPort] = localProxy
tcpProxyPool[localPort] = localProxy
localProxy.Run()
}

func prepareUDPHandler(localPort string, target string) {
local := fmt.Sprintf(":%s", localPort)
func prepareUDPHandler(localPort int, targetAddr *net.UDPAddr) {
local := fmt.Sprintf(":%d", localPort)
localAddr, err := net.ResolveUDPAddr("udp", local)
if err != nil {
log.Fatalln(err)
}
incomingPort, err := strconv.Atoi(localPort)
if err != nil {
log.Fatalln(err)
}
targetAddr, err := net.ResolveUDPAddr("udp", target)
if err != nil {
log.Fatalln(err)
}
localProxy, err := proxy.NewUDPProxy(localAddr, targetAddr)
if err != nil {
log.Fatalln(err)
}
udpProxyPool[incomingPort] = localProxy
udpProxyPool[localPort] = localProxy
localProxy.Run()
}

func closeAndDelete(localPort int) {
{
tcpProxy, ok := tcpProxyPool[localPort]
if ok {
tcpProxy.Close()
delete(tcpProxyPool, localPort)
func closeAndDelete(localPort string) {
var iPortStart int
var iPortEnd int
var err error
if strings.Index(localPort, portSep) >= 0 {
_, err = fmt.Sscanf(localPort, "%d/%d", &iPortStart, &iPortEnd)
if err != nil {
log.Fatalln(err)
}
}
{
udpProxy, ok := udpProxyPool[localPort]
if ok {
udpProxy.Close()
delete(udpProxyPool, localPort)
} else {
iPortStart, err = strconv.Atoi(localPort)
if err != nil {
log.Fatalln(err)
}
iPortEnd = iPortStart
}
for i := 0; (iPortStart + i) <= iPortEnd; i++ {
current := iPortStart + i
{
tcpProxy, ok := tcpProxyPool[current]
if ok {
tcpProxy.Close()
delete(tcpProxyPool, current)
}
}
{
udpProxy, ok := udpProxyPool[current]
if ok {
udpProxy.Close()
delete(udpProxyPool, current)
}
}
}
_, ok := configResult.Proxy[localPort]
if ok {
delete(configResult.Proxy, localPort)
}
}

Expand Down Expand Up @@ -129,8 +165,9 @@ func apiHandleProxyAdd(ctx *gin.Context) {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
go prepareTCPHandler(item.Local, item.Target)
go prepareUDPHandler(item.Local, item.Target)
addProxyItem(item.Local, item.Target)
// go prepareTCPHandler(item.Local, item.Target)
// go prepareUDPHandler(item.Local, item.Target)
ctx.JSON(http.StatusCreated, gin.H{
"message": "done",
})
Expand All @@ -155,49 +192,30 @@ func apiHandleProxyUpdate(ctx *gin.Context) {
return
}
local := ctx.Param("local")
incomingPort, err := strconv.Atoi(local)
if err != nil {
log.Fatalln(err)
}
closeAndDelete(incomingPort)
go prepareTCPHandler(local, item.Target)
go prepareUDPHandler(local, item.Target)
closeAndDelete(local)
addProxyItem(local, item.Target)
ctx.JSON(http.StatusCreated, gin.H{
"message": "done",
})
}

func apiHandleProxyDelete(ctx *gin.Context) {
local := ctx.Param("local")
incomingPort, err := strconv.Atoi(local)
if err != nil {
log.Fatalln(err)
}
closeAndDelete(incomingPort)
closeAndDelete(local)
ctx.JSON(http.StatusOK, gin.H{
"message": "done",
})
}

func apiHandleProxyList(ctx *gin.Context) {
result := make(map[string]string)
for localPort, tcpProxy := range tcpProxyPool {
result[fmt.Sprintf("%d", localPort)] = tcpProxy.BackendAddr().String()
}
ctx.JSON(http.StatusOK, result)
ctx.JSON(http.StatusOK, configResult.Proxy)
}

func apiHandleProxyDetail(ctx *gin.Context) {
local := ctx.Param("local")
incomingPort, err := strconv.Atoi(local)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
log.Fatalln(err)
}
tcpProxy := tcpProxyPool[incomingPort]
ctx.JSON(http.StatusOK, gin.H{
"local": local,
"target": tcpProxy.BackendAddr().String(),
"target": configResult.Proxy[local],
})
}

Expand All @@ -215,6 +233,7 @@ func main() {
udpProxyPool = make(map[int]*proxy.UDPProxy)
resolveConfig()
app := gin.Default()
app.UseRawPath = true
app.Use(cors.Default())
addAPIHandler(app)
app.Run(configResult.APIPort)
Expand Down
31 changes: 8 additions & 23 deletions web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,31 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<link
rel="stylesheet"
href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"
/>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css" />
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<title>简陋的web</title>
</head>
<body>
<div id="app">
<el-row>
<el-col :span="12" :offset="6">
<el-button @click="handleAdd" type="primary" plain
>添加一个</el-button
>
<el-button @click="handleAdd" type="primary" plain>添加一个</el-button>
<div style="float: right;">
<el-input placeholder="api host" v-model="api">
<template slot="prepend">api地址</template>
<el-button slot="append" @click="handleReload" plain>重新加载</el-button>
</el-input>
</div>
<el-table :data="tableData" style="width: 100%" row-key="local">
<el-table-column prop="local" label="local" width="120px">
</el-table-column>
<el-table-column prop="target" label="target" width="200px">
</el-table-column>
<el-table v-loading="loading" :data="tableData" style="width: 100%" row-key="local">
<el-table-column prop="local" label="local" width="120px"> </el-table-column>
<el-table-column prop="target" label="target" width="200px"> </el-table-column>
<el-table-column label="action">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleEdit(scope.row.local, scope.row.target)"
>编辑</el-button
>
<el-button size="mini" @click="handleEdit(scope.row.local, scope.row.target)">编辑</el-button>
<el-popover placement="top" width="160">
<p>确定删除吗?</p>
<div style="text-align: right; margin: 0">
<el-button
type="primary"
size="mini"
@click="handleDelete(scope.row.local)"
>确定</el-button
>
<el-button type="primary" size="mini" @click="handleDelete(scope.row.local)">确定</el-button>
</div>
<el-button size="mini" type="danger" slot="reference">删除</el-button>
</el-popover>
Expand Down
35 changes: 22 additions & 13 deletions web/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ new Vue({
isAdd: false,
form: {
target: '',
local: '',
}
}
local: ''
},
loading: true
};
},
methods: {
handleFormAction() {
if (this.isAdd) {
this.handleAddAction();
} else {

}
},
handleEdit(local, target) {
Expand All @@ -38,6 +38,9 @@ new Vue({
this.isAdd = true;
this.dialogFormVisible = true;
},
handleReload() {
this.loadList();
},
handleAddAction() {
this.dialogFormVisible = false;
this.addItem(this.form.local, this.form.target);
Expand All @@ -47,40 +50,46 @@ new Vue({
this.updateItem(this.form.local, this.form.target);
},
async loadList() {
this.loading = true;
const resp = await (await fetch(`${this.api}/proxy`, { mode: 'cors' })).json();
const keys = Object.keys(resp);
this.tableData = keys.map(key => {
this.tableData = keys.map(key => {
return {
local: key,
target: resp[key],
target: resp[key]
};
});
this.loading = false
},
async addItem(local, target) {
this.loading = true;
await fetch(`${this.api}/proxy`, {
method: 'POST',
body: JSON.stringify({ local, target }),
mode: 'cors',
mode: 'cors'
});
this.loadList();
},
async updateItem(local, target) {
await fetch(`${this.api}/proxy/${local}`, {
this.loading = true;
await fetch(`${this.api}/proxy/${encodeURIComponent(local)}`, {
method: 'PATCH',
body: JSON.stringify({ target }),
mode: 'cors',
mode: 'cors'
});
this.loadList();
},
async deleteItem(local) {
await fetch(`${this.api}/proxy/${local}`, {
this.loading = true;
console.warn(this);
await fetch(`${this.api}/proxy/${encodeURIComponent(local)}`, {
method: 'DELETE',
mode: 'cors',
mode: 'cors'
});
this.loadList();
},
}
},
mounted: function() {
this.loadList();
}
})
});

0 comments on commit 693ab64

Please sign in to comment.