域名解析 待完成

This commit is contained in:
xj
2024-03-06 23:34:21 +08:00
parent d8f2e79d70
commit 0b77b8ae1b
16 changed files with 124 additions and 53 deletions
@@ -24,7 +24,7 @@
</div>
<el-table :key='tableKey' :data="list" v-loading="listLoading" element-loading-text="给我一点时间" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" :label="$t('table.id')" width="50">
<el-table-column align="center" :label="$t('table.id')" width="80">
<template slot-scope="scope">
<span>{{ scope.row.id }}</span>
</template>
@@ -44,6 +44,11 @@
<span>{{ scope.row.protocal }}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.desc')" width="180">
<template slot-scope="scope">
<span>{{ scope.row.description }}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.domainName')" width="180">
<template slot-scope="scope">
<el-link target="_blank" @click="handleOpenWebPage(scope.row)">http(s)://{{ scope.row.domain }}</el-link>
@@ -59,11 +64,6 @@
<span>{{ scope.row.upLimitRate ? scope.row.upLimitRate : '--' }} / {{ scope.row.downLimitRate ? scope.row.downLimitRate : '--' }}</span>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('table.desc')" width="260">
<template slot-scope="scope">
<span>{{ scope.row.description }}</span>
</template>
</el-table-column>
<!-- <el-table-column width="150px" align="center" :label="$t('table.createTime')">-->
<!-- <template slot-scope="scope">-->
<!-- <span>{{ scope.row.createTime | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>-->
@@ -146,7 +146,7 @@
<el-input v-model="temp.clientIp"></el-input>
</el-form-item>
<el-form-item :label="$t('客户端端口')" prop="clientPort">
<el-input v-model="temp.clientPort"></el-input>
<el-input v-model="temp.clientPort" type="number" :step="1" controls-position="right" ></el-input>
</el-form-item>
<el-form-item :label="$t('域名')" prop="subdomain" v-if="(temp.protocal === 'HTTP' || temp.protocal === 'HTTP(S)') && domainName && domainName != ''">
<el-input v-model="temp.subdomain">
@@ -292,7 +292,9 @@ export default {
// { validator: isPortAvailable, trigger: 'change' }
],
clientIp: [{ required: true, message: '请输入客户端IP', trigger: 'blur' }],
clientPort: [{ required: true, message: '请输入客户端端口', trigger: 'blur' }],
clientPort: [{ required: true, message: '请输入客户端端口', trigger: 'blur' },
{ pattern: /^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/,message: '端口号请选择0-65535间的数字',trigger: 'blur'}
],
protocal: [{ required: true, message: '请选择协议', trigger: 'blur' }]
},
downloadLoading: false,
@@ -63,7 +63,8 @@
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form :rules="rules" ref="dataForm" :model="temp" label-position="left" label-width="70px" style='width: 400px; margin-left:50px;'>
<el-form-item :label="$t('table.port')" prop="port">
<el-input :placeholder="$t('table.portOrRange')" v-model="temp.port" :disabled="dialogStatus=='update'"></el-input>
<!-- <el-input :placeholder="$t('table.portOrRange')" v-model="temp.port" :disabled="dialogStatus=='update'"></el-input> -->
<el-input :placeholder="$t('table.portOrRange')" type="number" v-model="temp.port" :step="1" controls-position="right" :disabled="dialogStatus=='update'"></el-input>
</el-form-item>
<el-form-item :label="$t('table.group')" prop="group">
@@ -158,7 +159,10 @@ const calendarTypeOptions = [
dialogPvVisible: false,
pvData: [],
rules: {
port: [{ required: true, message: '端口必填', trigger: 'blur' }]
port: [
{ required: true, message: '端口必填', trigger: 'blur' },
{ pattern: /^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/,message: '端口号请选择0-65535间的数字',trigger: 'blur'}
]
},
downloadLoading: false,
checkBoxData:[], //表单勾选的行
@@ -44,7 +44,7 @@ neutrino:
# 是否启用SSL(注意:该配置必须和server-port对应上)
ssl-enable: ${SSL_ENABLE:true}
# 客户端连接唯一凭证
license-key: ${LICENSE_KEY:ce554b47de80495b9d1996cd62084973}
license-key: ${LICENSE_KEY:ac2987454da340d7a57217d69ed81565}
# 客户端唯一身份标识(可忽略,若不设置首次启动会自动生成)
client-id: ${CLIENT_ID:}
# 是否开启隧道传输报文日志(日志级别为debug时开启才有效)
@@ -26,6 +26,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.dromara.neutrinoproxy.server.dal.entity.LicenseDO;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyMapping;
import java.util.Date;
import java.util.List;
@@ -115,4 +116,6 @@ public interface LicenseMapper extends BaseMapper<LicenseDO> {
.eq(LicenseDO::getKey, licenseKey)
);
}
List<ProxyMapping> findEnableProxyMappingListByLicenseId(Integer licenseId);
}
@@ -31,6 +31,7 @@ import lombok.Data;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.dromara.neutrinoproxy.server.controller.res.proxy.PortMappingListRes;
import org.dromara.neutrinoproxy.server.proxy.domain.ProxyMapping;
import java.util.Date;
@@ -121,4 +122,5 @@ public class PortMappingDO {
BeanUtil.copyProperties(this, res);
return res;
}
}
@@ -9,6 +9,10 @@ import lombok.Data;
@Data
public class DomainMapping {
/**
* domainMapping 主键id
*/
private Integer id;
/**
* License
*/
@@ -26,7 +30,8 @@ public class DomainMapping {
public DomainMapping() {
}
public DomainMapping(Integer licenseId, String domain, String targetPath) {
public DomainMapping(Integer id, Integer licenseId, String domain, String targetPath) {
this.id = id;
this.licenseId = licenseId;
this.domain = domain;
this.targetPath = targetPath;
@@ -24,6 +24,10 @@ public class ProxyMapping {
* 客户端信息 IP:port
*/
private String lanInfo;
/**
* 协议
*/
private String protocal;
public static List<ProxyMapping> buildList(List<PortMappingDO> portMappingList) {
List<ProxyMapping> list = new ArrayList<>();
@@ -39,7 +43,8 @@ public class ProxyMapping {
public static ProxyMapping build(PortMappingDO portMappingDO) {
return new ProxyMapping()
.setServerPort(portMappingDO.getServerPort())
.setLanInfo(String.format("%s:%s", portMappingDO.getClientIp(), portMappingDO.getClientPort()));
.setLanInfo(String.format("%s:%s", portMappingDO.getClientIp(), portMappingDO.getClientPort()))
.setProtocal(portMappingDO.getProtocal());
}
}
@@ -9,6 +9,7 @@ import org.apache.commons.lang3.StringUtils;
import org.dromara.neutrinoproxy.core.Constants;
import org.dromara.neutrinoproxy.core.util.HttpUtil;
import org.dromara.neutrinoproxy.core.util.IpUtil;
import org.dromara.neutrinoproxy.server.proxy.domain.DomainMapping;
import org.dromara.neutrinoproxy.server.service.PortMappingService;
import org.dromara.neutrinoproxy.server.service.SecurityGroupService;
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
@@ -23,25 +24,18 @@ import org.noear.solon.Solon;
public class HttpVisitorSecurityChannelHandler extends ChannelInboundHandlerAdapter {
private final SecurityGroupService securityGroupService = Solon.context().getBean(SecurityGroupService.class);
private final PortMappingService portMappingService = Solon.context().getBean(PortMappingService.class);
// /**
// * 域名
// */
// private String domainName;
// public HttpVisitorSecurityChannelHandler(String domainName) {
// this.domainName = domainName;
// }
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
String domain = ctx.channel().attr(Constants.DOMAIN).get();
if (StringUtils.isBlank(domain)) {
Integer domainId = ctx.channel().attr(Constants.SERVER_PORT).get();
if (null == domainId) {
// 获取Host请求头
byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
String httpContent = new String(bytes);
domain = HttpUtil.getHostIgnorePort(httpContent);
String domain = HttpUtil.getHostIgnorePort(httpContent);
log.debug("HttpProxy host: {}", domain);
if (StringUtils.isBlank(domain)) {
@@ -53,19 +47,20 @@ public class HttpVisitorSecurityChannelHandler extends ChannelInboundHandlerAdap
ctx.channel().close();
return;
}
DomainMapping dm = ProxyUtil.domainMapingMap.get(domain);
// 判断IP是否在该端口绑定的安全组允许的规则内
String ip = IpUtil.getRealRemoteIp(httpContent);
if (ip == null) {
ip = IpUtil.getRemoteIp(ctx);
}
// if (!securityGroupService.judgeAllow(ip, portMappingService.getSecurityGroupIdByMappingPort(serverPort))) {
// // 不在安全组规则放行范围内
// ctx.channel().close();
// return;
// }
if (null==dm.getId() || !securityGroupService.judgeAllow(ip, portMappingService.getSecurityGroupIdByMappingPort(dm.getId()))) {
// 不在安全组规则放行范围内
ctx.channel().close();
return;
}
ctx.channel().attr(Constants.REAL_REMOTE_IP).set(ip);
ctx.channel().attr(Constants.DOMAIN).set(domain);
ctx.channel().attr(Constants.SERVER_PORT).set(dm.getId());
}
// 继续传播
@@ -159,7 +159,8 @@ public class DomainMappingService implements LifecycleBean {
// visitorChannelService.addVisitorChannelByPortMapping(domainMappingDO);
// 更新域名映射
// ProxyUtil.setSubdomainToServerPort(domainMappingDO.getDomain(), domainMappingDO.getServerPort());
ProxyUtil.domainMapingMap.put(domainMappingDO.getDomain(), new DomainMapping(domainMappingDO.getLicenseId(), domainMappingDO.getDomain(), domainMappingDO.getTargetPath()));
ProxyUtil.domainMapingMap.put(domainMappingDO.getDomain(), new DomainMapping(domainMappingDO.getId(), domainMappingDO.getLicenseId(), domainMappingDO.getDomain(), domainMappingDO.getTargetPath()));
return new PortMappingCreateRes();
}
@@ -252,7 +253,7 @@ public class DomainMappingService implements LifecycleBean {
if(CollectionUtil.isNotEmpty(allMappingDOList)) {
allMappingDOList.forEach(domain -> {
ProxyUtil.domainMapingMap.put(domain.getDomain(),
new DomainMapping(domain.getLicenseId(), domain.getDomain(), domain.getTargetPath()));
new DomainMapping(domain.getId(), domain.getLicenseId(), domain.getDomain(), domain.getTargetPath()));
});
}
}
@@ -130,6 +130,7 @@ public class PortPoolService {
log.warn("bulk add port err:{}",e.getMessage());
}
}
}else{
throw ServiceException.create(PORT_RANGE_FAIL);
}
@@ -59,17 +59,18 @@ public class VisitorChannelService {
private PortPoolMapper portPoolMapper;
/**
* 初始化
* 初始化 - 域名id/端口号,客户端通道绑定初始化
* @param licenseId
*/
public void initVisitorChannel(Integer licenseId, Channel cmdChannel) {
List<PortMappingDO> portMappingList = portMappingMapper.findEnableListByLicenseId(licenseId);
// List<PortMappingDO> portMappingList = portMappingMapper.findEnableListByLicenseId(licenseId);
List<ProxyMapping> proxyMappingList = licenseMapper.findEnableProxyMappingListByLicenseId(licenseId);
// 没有端口映射仍然保持连接
ProxyUtil.initProxyInfo(licenseId, ProxyMapping.buildList(portMappingList));
ProxyUtil.initProxyInfo(licenseId, proxyMappingList); //ProxyMapping.buildList(portMappingList));
ProxyUtil.addCmdChannel(licenseId, cmdChannel, portMappingList.stream().map(PortMappingDO::getServerPort).collect(Collectors.toSet()));
ProxyUtil.addCmdChannel(licenseId, cmdChannel, proxyMappingList.stream().map(ProxyMapping::getServerPort).collect(Collectors.toSet()));
startUserPortServer(ProxyUtil.getAttachInfo(cmdChannel), portMappingList);
startUserPortServer(ProxyUtil.getAttachInfo(cmdChannel), proxyMappingList); // portMappingList
}
/**
@@ -184,7 +185,7 @@ public class VisitorChannelService {
// 未删除且未禁用,则开启代理
ProxyUtil.addProxyInfo(portMappingDO.getLicenseId(), ProxyMapping.build(portMappingDO));
ProxyUtil.addCmdChannel(portMappingDO.getLicenseId(), cmdChannel, Sets.newHashSet(portMappingDO.getServerPort()));
startUserPortServer(ProxyUtil.getAttachInfo(cmdChannel), Lists.newArrayList(portMappingDO));
startUserPortServer(ProxyUtil.getAttachInfo(cmdChannel), Lists.newArrayList(ProxyMapping.build(portMappingDO)));
}
}
}
@@ -217,26 +218,26 @@ public class VisitorChannelService {
ProxyUtil.removeProxyInfo(portMappingDO.getServerPort());
}
private void startUserPortServer(CmdChannelAttachInfo cmdChannelAttachInfo, List<PortMappingDO> portMappingList) {
if (CollectionUtil.isEmpty(portMappingList)) {
private void startUserPortServer(CmdChannelAttachInfo cmdChannelAttachInfo, List<ProxyMapping> proxyMappingList) {
if (CollectionUtil.isEmpty(proxyMappingList)) {
return;
}
for (PortMappingDO portMapping : portMappingList) {
if (EnableStatusEnum.DISABLE.getStatus().equals(portMapping.getEnable())) {
// 端口映射被禁用了,忽略 TODO 映射没被禁用,但端口被禁用了也需要处理
continue;
}
for (ProxyMapping proxyMapping : proxyMappingList) {
// if (EnableStatusEnum.DISABLE.getStatus().equals(proxyMapping.getEnable())) {
// // 端口映射被禁用了,忽略 TODO 映射没被禁用,但端口被禁用了也需要处理 修改方案 20240306查询时就处理了【端口禁用】和【端口映射禁用】的情况,待验证
// continue;
// }
try {
proxyMutualService.bindServerPort(cmdChannelAttachInfo, portMapping.getServerPort());
proxyMutualService.bindServerPort(cmdChannelAttachInfo, proxyMapping.getServerPort());
NetworkProtocolEnum networkProtocolEnum = NetworkProtocolEnum.of(portMapping.getProtocal());
NetworkProtocolEnum networkProtocolEnum = NetworkProtocolEnum.of(proxyMapping.getProtocal());
if (networkProtocolEnum == NetworkProtocolEnum.UDP) {
udpServerBootstrap.bind(portMapping.getServerPort()).get();
log.info("bind UDP user port {}", portMapping.getServerPort());
} else {
tcpServerBootstrap.bind(portMapping.getServerPort()).get();
log.info("bind TCP user port {}", portMapping.getServerPort());
udpServerBootstrap.bind(proxyMapping.getServerPort()).get();
log.info("bind UDP user port {}", proxyMapping.getServerPort());
} else if (proxyMapping.getServerPort() <= 65535){
tcpServerBootstrap.bind(proxyMapping.getServerPort()).get();
log.info("bind TCP user port {}", proxyMapping.getServerPort());
}
} catch (Exception ex) {
// BindException表示该端口已经绑定过
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.neutrinoproxy.server.dal.LicenseMapper">
<select id="findEnableProxyMappingListByLicenseId" resultType="org.dromara.neutrinoproxy.server.proxy.domain.ProxyMapping">
SELECT pm.server_port as serverPort,CONCAT(pm.client_ip, ':', pm.client_port) as lanInfo, pm.protocal as protocal
FROM port_mapping pm
LEFT JOIN port_pool pp on pm.server_port = pp.port
WHERE pm.enable = 1 AND pm.is_online = 1 AND pp.enable = 1
UNION ALL
SELECT dm.id as serverPort,dm.target_path as lanInfo, 'HTTP' as protocal
FROM domain_mapping dm
LEFT JOIN license l on dm.license_id = l.`id`
WHERE dm.enable = 1 AND l.is_online = 1
</select>
</mapper>
@@ -230,7 +230,7 @@ CREATE INDEX IF NOT EXISTS I_flow_report_month_date ON flow_report_month(`date`)
CREATE INDEX IF NOT EXISTS I_flow_report_month_user_id ON flow_report_month(`user_id`);
CREATE INDEX IF NOT EXISTS I_flow_report_month_license_id ON flow_report_month(`license_id`);
#
# AUTO_INCREMENT 65550 0-65535
CREATE TABLE IF NOT EXISTS `domain_mapping` (
`id` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
`license_id` INTEGER(20) NOT NULL,
@@ -245,5 +245,5 @@ CREATE TABLE IF NOT EXISTS `domain_mapping` (
`security_group_id` INTEGER(20) NOT NULL DEFAULT 0,
`create_time` TIMESTAMP NOT NULL,
`update_time` TIMESTAMP NOT NULL
);
) AUTO_INCREMENT = 65550;
CREATE UNIQUE INDEX IF NOT EXISTS I_domain_mapping_domain ON domain_mapping (`domain` ASC);
@@ -242,3 +242,22 @@ CREATE TABLE IF NOT EXISTS `flow_report_month` (
KEY `I_flow_report_month_user_id` (`user_id`),
KEY `I_flow_report_month_license_id` (`license_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
# AUTO_INCREMENT 65550 0-65535
CREATE TABLE IF NOT EXISTS `domain_mapping` (
`id` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
`license_id` INTEGER(20) NOT NULL,
`protocal` VARCHAR(10) NOT NULL DEFAULT 'HTTP',
`domain` VARCHAR(50) DEFAULT NULL,
`target_path` VARCHAR(50) NOT NULL,
`up_limit_rate` VARCHAR(20) DEFAULT NULL,
`down_limit_rate` VARCHAR(20) DEFAULT NULL,
`description` VARCHAR(100) DEFAULT NULL,
`request_header` INTEGER(20) NOT NULL DEFAULT 0,
`enable` INTEGER(2) NOT NULL,
`security_group_id` INTEGER(20) NOT NULL DEFAULT 0,
`create_time` TIMESTAMP NOT NULL,
`update_time` TIMESTAMP NOT NULL
) AUTO_INCREMENT = 65550;
CREATE UNIQUE INDEX IF NOT EXISTS I_domain_mapping_domain ON domain_mapping (`domain` ASC);
@@ -243,3 +243,20 @@ CREATE TABLE IF NOT EXISTS `flow_report_month` (
KEY `I_flow_report_month_license_id` (`license_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
# AUTO_INCREMENT 65550 0-65535
CREATE TABLE IF NOT EXISTS `domain_mapping` (
`id` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
`license_id` INTEGER(20) NOT NULL,
`protocal` VARCHAR(10) NOT NULL DEFAULT 'HTTP',
`domain` VARCHAR(50) DEFAULT NULL,
`target_path` VARCHAR(50) NOT NULL,
`up_limit_rate` VARCHAR(20) DEFAULT NULL,
`down_limit_rate` VARCHAR(20) DEFAULT NULL,
`description` VARCHAR(100) DEFAULT NULL,
`request_header` INTEGER(20) NOT NULL DEFAULT 0,
`enable` INTEGER(2) NOT NULL COMMENT '是否启用(1、启用 2、禁用)',
`security_group_id` INTEGER(20) NOT NULL DEFAULT 0,
`create_time` TIMESTAMP NOT NULL,
`update_time` TIMESTAMP NOT NULL
) AUTO_INCREMENT = 65550;
CREATE UNIQUE INDEX IF NOT EXISTS I_domain_mapping_domain ON domain_mapping (`domain` ASC);