~ 上钩列表分页优化

~ 上钩列表支持批量删除
~ 上钩列表长度展示
~ 上钩列表支持集群筛选
~ 集群页面支持删除
~ 改用 IPIP 本地库获取地理信息
~ 支持 WebHook Api 通知
~ 提供获取上钩列表 API
~ WEB,暗网蜜罐 API 移到各种服务上,抛开 Admin
~ 修复暗网蜜罐使用问题
~ 修复集群取 IP 错误问题
~ SSH 高交互支持
This commit is contained in:
SanJin 2019-08-25 01:00:42 +08:00
parent 863ebe53f7
commit b97dae897a
57 changed files with 3814 additions and 255 deletions

View File

@ -40,10 +40,16 @@
margin-top: 0px;
}
.agent_name{
.agent_name {
font-weight: bold;
color: #167bcc;
}
.fa-trash-o {
cursor: pointer;
color: #dc0e0e;
margin-right: 5px;
}
</style>
<div class="row">
<div class="col-sm-12">
@ -64,7 +70,7 @@
<th width="10%" style="text-align: center;">Mysql</th>
<th width="10%" style="text-align: center;">Telnet</th>
<th width="10%" style="text-align: center;">FTP</th>
{{/*<th width="10%" style="text-align: center;">Http代理</th>*/}}
<th width="1%"></th>
</tr>
</thead>
<tbody id="tableList">
@ -79,6 +85,28 @@
{{template "footer" }}
<script>
function del(id) {
$.ajax({
type: "POST",
url: "/post/colony/del",
dataType: "json",
data: {
"id": id
},
success: function (e) {
if (e.code == 200) {
window.location.href = "/colony";
} else {
swal("删除失败", e.msg, 'error');
}
},
error: function (e) {
swal("删除失败", "请 Github 提交 Issues", 'error');
}
});
}
$.ajax({
type: "GET",
url: "/get/colony/list",
@ -148,6 +176,10 @@
// } else {
// _h += ' <td class="td" style="text-align: center;"><span class="closex"></span></td>';
// }
_h += ' <td class="td" style="text-align: center;">';
_h += ' <i class="fa fa-trash-o" onclick="del(' + data[i].id + ')"></i>';
_h += ' </td>';
} else {
_h += ' <td class="td agent_name"><span class="closex"></span>' + data[i].agent_name + '</td>';
_h += ' <td class="td"><span>' + data[i].agent_ip + '</span></td>';
@ -159,6 +191,9 @@
_h += ' <td class="td" style="text-align: center;"><span class="closex"></span></td>';
_h += ' <td class="td" style="text-align: center;"><span class="closex"></span></td>';
// _h += ' <td class="td" style="text-align: center;"><span class="closex"></span></td>';
_h += ' <td class="td" style="text-align: center;">';
_h += ' <i class="fa fa-trash-o" onclick="del(' + data[i].id + ')"></i>';
_h += ' </td>';
}

View File

@ -1,4 +1,5 @@
{{template "header"}}
<link href="/static/libs/page/jquery.sPage.css" rel="stylesheet"/>
<style>
.card-box {
padding: 0px;
@ -24,8 +25,7 @@
}
.fa-trash-o {
color: #f00;
cursor: pointer;
margin-right: 5px;
}
.info {
@ -96,20 +96,38 @@
}
.agent_name {
border: 1px solid #167bcc;
border: 1px solid #079dfd;
padding: 2px 5px;
border-radius: 5px;
color: #167bcc;
color: #079dfd;
font-size: 12px;
}
.ipinfo{
.ipinfo {
border: 1px solid #434e56;
padding: 2px 5px;
border-radius: 5px;
color: #434e56;
font-size: 12px;
}
.cinfo {
border: 1px solid #1eaec1;
padding: 2px 5px;
border-radius: 5px;
color: #1eaec1;
font-size: 12px;
}
#myPage {
text-align: center;
margin-bottom: 20px;
}
.checkbox {
padding-left: 20px;
margin-bottom: -2px;
}
</style>
<div class="row">
<div class="col-sm-12">
@ -126,6 +144,7 @@
<select class="form-control" id="selectType" style="height: 34px;" onchange="so()"></select>
</div>
<div class="col-sm-2">
<select class="form-control" id="selectColony" style="height: 34px;" onchange="so()"></select>
</div>
<div class="col-sm-2">
</div>
@ -146,22 +165,28 @@
<table class="table table-hover">
<thead>
<tr>
<th width="10%">项目</th>
<th width="10%">集群名称</th>
<th width="10%">来源 IP</th>
<th width="10%">地理信息</th>
<th width="10%">信息</th>
<th width="1%">
<div class="checkbox checkbox-single checkbox-danger">
<input type="checkbox" value="all" id="check_all">
<label></label>
</div>
</th>
<th width="11%">项目</th>
<th width="8%">集群名称</th>
<th width="8%">来源 IP</th>
<th width="8%">地理信息</th>
<th width="5%">信息</th>
<th width="5%">长度</th>
<th width="8%">上钩时间</th>
<th width="2%">操作</th>
</tr>
</thead>
<tbody id="tableList"></tbody>
</table>
</div>
<div style="text-align: center;" class="dataTables_paginate paging_simple_numbers">
<ul class="pagination" id="pages">
</ul>
</div>
<button type="button" id="delbtn" class="btn btn-danger waves-effect waves-light btn-sm"
style="float: left;margin-top: 5px;" onclick="del()" disabled><i class="fa fa-trash-o"></i>删除
</button>
<div id="myPage"></div>
</div>
</div>
@ -180,6 +205,7 @@
</div>
{{template "footer" }}
<script src="/static/libs/page/jquery.sPage.js"></script>
<script>
function show(id) {
$.ajax({
@ -205,13 +231,21 @@
$('#myModal').modal('show')
}
function del(id) {
function del() {
var val = "";
$('input[name^="check_"]').each(function () {
if (this.checked) {
val += $(this).val() + ',';
}
});
val = val.substring(0, val.length - 1);
$.ajax({
type: "POST",
url: "/post/fish/del",
dataType: "json",
data: {
"id": id
"id": val
},
success: function (e) {
if (e.code == 200) {
@ -232,26 +266,65 @@
url: "/get/fish/typeList",
dataType: "json",
success: function (e) {
if (e.code == 200) {
var data = e.data;
var _h = '<option value="all">请选择类型</option>';
for (var i = 0; i < data.length; i++) {
_h += '<option value="' + data[i].type + '">' + data[i].type + '</option>';
}
$("#selectType").html(_h);
} else {
var data = e.resultInfoType;
var _h = '<option value="all">请选择类型</option>';
for (var i = 0; i < data.length; i++) {
_h += '<option value="' + data[i].type + '">' + data[i].type + '</option>';
}
$("#selectType").html(_h);
var data1 = e.resultColonyName;
var _h1 = '<option value="all">请选择集群</option>';
_h1 += '<option value="本机">本机</option>';
for (var i = 0; i < data1.length; i++) {
_h1 += '<option value="' + data1[i].agent_name + '">' + data1[i].agent_name + '</option>';
}
$("#selectColony").html(_h1);
},
error: function (e) {
}
});
}
function init(page, type, so_text) {
$("#check_all").click(function () {
var status = 0;
$('input[name="check_x"]').each(function () {
if ($("#check_all").prop('checked') == true) {
status++;
this.checked = true;
} else {
this.checked = false;
}
});
if (status > 0) {
$("#delbtn").attr("disabled", false);
} else {
$("#delbtn").attr("disabled", true);
}
});
function changedel() {
var status = 0;
$('input[name="check_x"]').each(function () {
if (this.checked == true) {
status++;
} else {
}
});
if (status > 0) {
$("#delbtn").attr("disabled", false);
} else {
$("#delbtn").attr("disabled", true);
}
}
function init(page, type, colony, so_text) {
$.ajax({
type: "GET",
url: "/get/fish/list",
@ -260,6 +333,7 @@
"page": page,
"pageSize": 10,
"type": type,
"colony": colony,
"so_text": so_text
},
success: function (e) {
@ -268,6 +342,7 @@
for (var i = 0; i < data.length; i++) {
_h += '<tr>';
_h += ' <td class="td"><div class="checkbox checkbox-single checkbox-danger"><input type="checkbox" value="' + data[i].id + '" name="check_x" onchange="changedel()"><label></label></div></td>'
_h += ' <td class="td">';
if (data[i].type == "WEB") {
_h += ' <span class="label label-primary">WEB</span> ';
@ -289,12 +364,20 @@
_h += ' </td>';
_h += ' <td class="td"><span class="agent_name">' + data[i].agent + '</span></td>';
_h += ' <td class="td" style="font-weight: bold;">' + data[i].ip + '</td>';
_h += ' <td class="td"><span class="ipinfo">' + data[i].ip_info + '</span></td>';
// country,region,city
if (data[i].country == "本机地址") {
_h += ' <td class="td"><span class="ipinfo">本机地址</span></td>';
} else if (data[i].country == "局域网") {
_h += ' <td class="td"><span class="ipinfo">局域网</span></td>';
}
else {
_h += ' <td class="td"><span class="ipinfo">' + data[i].country + ' ' + data[i].region + ' ' + data[i].city + '</span></td>';
}
_h += ' <td><span class="info" onclick="show(' + data[i].id + ')">点击查看</span></td>';
_h += ' <td class="td"><span class="cinfo">' + data[i].info.length + '</span></td>';
_h += ' <td class="td">' + data[i].create_time + '</td>';
_h += ' <td class="td" style="text-align: center;">';
_h += ' <i class="fa fa-trash-o" onclick="del(' + data[i].id + ')"></i>';
_h += ' </td>';
_h += '</tr>';
}
@ -305,44 +388,43 @@
$("#tableList").html(_h);
var pageCount = e.pageCount;
var totalCount = e.totalCount;
var page = e.page;
var _hx = '';
for (var i = 0; i < parseInt(pageCount); i++) {
if (page == (i + 1)) {
_hx += '<li class="paginate_button page-item active">';
_hx += ' <a onclick="page(' + (i + 1) + ')" style="cursor: pointer;" aria-controls="datatable" data-dt-idx="' + (i + 1) + '" tabindex="0" class="page-link">' + (i + 1) + '</a>';
_hx += '</li>';
} else {
_hx += '<li class="paginate_button page-item">';
_hx += ' <a onclick="page(' + (i + 1) + ')" style="cursor: pointer;" aria-controls="datatable" data-dt-idx="' + (i + 1) + '" tabindex="0" class="page-link">' + (i + 1) + '</a>';
_hx += '</li>';
$("#myPage").sPage({
page: page,
pageSize: 10,
total: totalCount,
totalTxt: "共{total}条",//数据总条数文字描述,{total}为占位符,默认"共{total}条"
noData: true,//没有数据时是否显示分页默认false不显示true显示第一页
showTotal: true,//是否显示总条数默认关闭false
showSkip: true,
showPN: true,
prevPage: "上一页",
nextPage: "下一页",
backFun: function (p) {
var selectType = $("#selectType").val();
var selectColony = $("#selectColony").val();
var so_text = $("#so_text").val();
init(p, selectType, selectColony, so_text);
}
}
$("#pages").html(_hx);
});
},
error: function (e) {
}
});
}
init(1, "all", "");
init(1, "all", "all", "");
init_type();
function so() {
var selectType = $("#selectType").val();
var selectColony = $("#selectColony").val();
var so_text = $("#so_text").val();
init(1, selectType, so_text);
}
function page(p) {
var selectType = $("#selectType").val();
var so_text = $("#so_text").val();
init(p, selectType, so_text);
init(1, selectType, selectColony, so_text);
}
</script>

View File

@ -6,7 +6,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="/static/favicon.ico">
<title>HFish - 扩展企业安全测试主动诱导型蜜罐框架系统</title>
<title>HFish - 扩展企业安全测试主动诱导型开源蜜罐框架系统</title>
<link href="/static/css/style.css" rel="stylesheet" type="text/css"/>
<link href="/static/libs/bootstrap-sweetalert/sweet-alert.css" rel="stylesheet" type="text/css"/>
<link href="/static/libs/switchery/switchery.min.css" rel="stylesheet"/>

View File

@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="/static/favicon.ico">
<title>HFish - 扩展企业安全测试主动诱导型蜜罐框架系统</title>
<title>HFish - 扩展企业安全测试主动诱导型开源蜜罐框架系统</title>
<link href="/static/css/style.css" rel="stylesheet" type="text/css"/>
<link href="/static/libs/bootstrap-sweetalert/sweet-alert.css" rel="stylesheet" type="text/css"/>
<style>

View File

@ -39,6 +39,14 @@
width: 120px;
}
.sweet-alert {
z-index: 99999;
}
.w-e-text-container {
z-index: 999;
}
</style>
<div class="row">

View File

@ -9,7 +9,7 @@ account = admin # 登录账号
password = admin # 登录密码
[api]
status = 1 # 是否启动 API 1 启动 0 关闭
status = 0 # 是否启动 API 1 启动 0 关闭
web_url = /api/v1/post/report # 管理后台启动地址
deep_url = /api/v1/post/deep_report # 管理后台启动地址
sec_key = 9cbf8a4dcb8e30682b927f352d6559a0 # API 认证秘钥
@ -24,18 +24,18 @@ url = / # WEB 访问目录,默认 / 可
[deep]
status = 0 # 是否启动 暗网 1 启动 0 关闭, 启动 API 后 方可上报结果
addr = 0.0.0.0:9002 # 暗网 WEB 启动地址
addr = 0.0.0.0:8080 # 暗网 WEB 启动地址
template = deep/html # 暗网 WEB 模板路径
index = index.html # 暗网 WEB 首页文件
static = deep/static # 暗网 WEB 静态文件路径 注意必须存在两个目录html 文件 和静态文件 不能平级
url = / # 暗网 WEB 访问目录,默认 / 可更改成 index.html index.asp index.php
[ssh]
status = 0 # 是否启动 SSH 1 启动 0 关闭
status = 2 # 是否启动 SSH 0 关闭 1 低交互 2 高交互
addr = 0.0.0.0:22 # SSH 服务端地址 注意端口冲突,请先关闭服务器 openssh 服务 或 修改端口
[redis]
status = 0 # 是否启动 Redis 1 启动 0 关闭
status = 1 # 是否启动 Redis 1 启动 0 关闭
addr = 0.0.0.0:6379 # Redis 服务端地址 注意端口冲突
[mysql]

View File

@ -2,15 +2,56 @@ package ssh
import (
"github.com/gliderlabs/ssh"
"HFish/core/report"
"golang.org/x/crypto/ssh/terminal"
"fmt"
"io"
"strings"
"HFish/utils/log"
"HFish/utils/is"
"HFish/core/rpc/client"
"HFish/core/report"
"HFish/utils/log"
"HFish/utils/conf"
"HFish/utils/json"
"github.com/bitly/go-simplejson"
"HFish/utils/file"
)
func getJson() *simplejson.Json {
res, err := json.Get("ssh")
if err != nil {
log.Pr("HFish", "127.0.0.1", "解析 SSH JSON 文件失败", err)
}
return res
}
func Start(addr string) {
ssh.ListenAndServe(addr, nil,
ssh.ListenAndServe(
addr,
func(s ssh.Session) {
res := getJson()
term := terminal.NewTerminal(s, res.Get("hostname").MustString())
line := ""
for {
line, _ = term.ReadLine()
if line == "exit" {
break
}
fileName := res.Get("command").Get(line).MustString()
if (fileName == "") {
fileName = res.Get("command").Get("default").MustString()
}
output := file.ReadLibsText("ssh", fileName)
fmt.Println(line)
io.WriteString(s, output+"\n")
}
},
ssh.PasswordAuth(func(s ssh.Context, password string) bool {
info := s.User() + "&&" + password
@ -25,7 +66,21 @@ func Start(addr string) {
go report.ReportSSH(arr[0], "本机", info)
}
return false // false 代表 账号密码 不正确
sshStatus := conf.Get("ssh", "status")
if (sshStatus == "2") {
// 高交互模式
res := getJson()
accountx := res.Get("account")
passwordx := res.Get("password")
if (accountx.MustString() == s.User() && passwordx.MustString() == password) {
return true
}
}
// 低交互模式,返回账号密码不正确
return false
}),
)
}

View File

@ -7,31 +7,107 @@ import (
"strings"
"HFish/utils/send"
"strconv"
"HFish/utils/try"
"encoding/json"
"bytes"
"net/http"
"HFish/utils/log"
)
func alert(title string, agent string, ipx string, infox string) {
sql := `select status,info from hfish_setting where type = "alertMail"`
isAlertStatus := dbUtil.Query(sql)
type HFishInfo struct {
id string
model string
project string
typex string
agent string
ip string
country string
region string
city string
info string
time string
}
status := strconv.FormatInt(isAlertStatus[0]["status"].(int64), 10)
func alert(id string, model string, typex string, projectName string, agent string, ipx string, country string, region string, city string, infox string, time string) {
// 判断邮件通知
try.Try(func() {
// 只有新加入才会发送邮件通知
if (model == "new") {
sql := `select status,info from hfish_setting where type = "alertMail"`
isAlertStatus := dbUtil.Query(sql)
// 判断是否启用通知
if status == "1" {
info := isAlertStatus[0]["info"]
config := strings.Split(info.(string), "&&")
status := strconv.FormatInt(isAlertStatus[0]["status"].(int64), 10)
text := `
<div><b>Hi上钩了</b></div>
<div><b><br /></b></div>
<div><b>集群名称</b>` + agent + `</div>
<div><b>攻击IP</b>` + ipx + `</div>
<div><b>上钩内容</b>` + infox + `</div>
<div><br /></div>
<div><span style="color: rgb(128, 128, 128); font-size: 10px;">(HFish 自动发送)</span></div>
`
// 判断是否启用通知
if status == "1" {
info := isAlertStatus[0]["info"]
config := strings.Split(info.(string), "&&")
send.SendMail(config[4:], "[HFish]提醒你,"+title+"有鱼上钩!", text, config)
}
if (country == "本地地址") {
region = ""
city = ""
} else if (country == "局域网") {
region = ""
city = ""
}
text := `
<div><b>Hi上钩了</b></div>
<div><b><br /></b></div>
<div><b>集群名称</b>` + agent + `</div>
<div><b>攻击IP</b>` + ipx + `</div>
<div><b>地理信息</b>` + country + ` ` + region + ` ` + city + `</div>
<div><b>上钩内容</b>` + infox + `</div>
<div><br /></div>
<div><span style="color: rgb(128, 128, 128); font-size: 10px;">(HFish 自动发送)</span></div>
`
send.SendMail(config[4:], "[HFish]提醒你,"+typex+"有鱼上钩!", text, config)
}
}
}).Catch(func() {
})
// 判断 WebHook 通知
try.Try(func() {
sql := `select status,info from hfish_setting where type = "webHook"`
isAlertStatus := dbUtil.Query(sql)
status := strconv.FormatInt(isAlertStatus[0]["status"].(int64), 10)
// 判断是否启用通知
if status == "1" {
info := isAlertStatus[0]["info"]
fishInfo := HFishInfo{
id,
model,
"",
"",
"",
"",
"",
"",
"",
"",
"",
}
b, _ := json.Marshal(fishInfo)
body := bytes.NewBuffer(b)
resp, err := http.Post(info.(string), "application/json;charset=utf-8", body)
if err != nil {
log.Pr("HFish", "127.0.0.1", "WebHook 调用失败", err)
} else {
log.Pr("HFish", "127.0.0.1", "WebHook 调用成功")
}
defer resp.Body.Close()
}
}).Catch(func() {
})
}
// 上报 集群 状态
@ -71,74 +147,80 @@ func ReportAgentStatus(agentName string, agentIp string, webStatus string, deepS
// 上报 WEB
func ReportWeb(projectName string, agent string, ipx string, info string) {
ipInfo := ip.Get(ipx)
sql := `INSERT INTO hfish_info(type,project_name,agent,ip,ip_info,info,create_time) values(?,?,?,?,?,?,?);`
dbUtil.Insert(sql, "WEB", projectName, agent, ipx, ipInfo, info, time.Now().Format("2006-01-02 15:04:05"))
go alert("WEB", agent, ipx, info)
country, region, city := ip.GetIp(ipx)
sql := `INSERT INTO hfish_info(type,project_name,agent,ip,country,region,city,info,create_time) values(?,?,?,?,?,?,?,?,?);`
id := dbUtil.Insert(sql, "WEB", projectName, agent, ipx, country, region, city, info, time.Now().Format("2006-01-02 15:04:05"))
go alert(strconv.FormatInt(id, 10), "new", "WEB", projectName, agent, ipx, country, region, city, info, time.Now().Format("2006-01-02 15:04:05"))
}
// 上报 暗网 WEB
func ReportDeepWeb(projectName string, agent string, ipx string, info string) {
ipInfo := ip.Get(ipx)
sql := `INSERT INTO hfish_info(type,project_name,agent,ip,ip_info,info,create_time) values(?,?,?,?,?,?,?);`
dbUtil.Insert(sql, "DEEP", projectName, agent, ipx, ipInfo, info, time.Now().Format("2006-01-02 15:04:05"))
go alert("DEEP", agent, ipx, info)
country, region, city := ip.GetIp(ipx)
sql := `INSERT INTO hfish_info(type,project_name,agent,ip,country,region,city,info,create_time) values(?,?,?,?,?,?,?,?,?);`
id := dbUtil.Insert(sql, "DEEP", projectName, agent, ipx, country, region, city, info, time.Now().Format("2006-01-02 15:04:05"))
go alert(strconv.FormatInt(id, 10), "new", "DEEP", projectName, agent, ipx, country, region, city, info, time.Now().Format("2006-01-02 15:04:05"))
}
// 上报 SSH
func ReportSSH(ipx string, agent string, info string) {
ipInfo := ip.Get(ipx)
sql := `INSERT INTO hfish_info(type,project_name,agent,ip,ip_info,info,create_time) values(?,?,?,?,?,?,?);`
dbUtil.Insert(sql, "SSH", "SSH蜜罐", agent, ipx, ipInfo, info, time.Now().Format("2006-01-02 15:04:05"))
go alert("SSH", agent, ipx, info)
country, region, city := ip.GetIp(ipx)
sql := `INSERT INTO hfish_info(type,project_name,agent,ip,country,region,city,info,create_time) values(?,?,?,?,?,?,?,?,?);`
id := dbUtil.Insert(sql, "SSH", "SSH蜜罐", agent, ipx, country, region, city, info, time.Now().Format("2006-01-02 15:04:05"))
go alert(strconv.FormatInt(id, 10), "new", "SSH", "SSH蜜罐", agent, ipx, country, region, city, info, time.Now().Format("2006-01-02 15:04:05"))
}
// 上报 Redis
func ReportRedis(ipx string, agent string, info string) int64 {
ipInfo := ip.Get(ipx)
sql := `INSERT INTO hfish_info(type,project_name,agent,ip,ip_info,info,create_time) values(?,?,?,?,?,?,?);`
go alert("REDIS", agent, ipx, info)
return dbUtil.Insert(sql, "REDIS", "Redis蜜罐", agent, ipx, ipInfo, info, time.Now().Format("2006-01-02 15:04:05"))
country, region, city := ip.GetIp(ipx)
sql := `INSERT INTO hfish_info(type,project_name,agent,ip,country,region,city,info,create_time) values(?,?,?,?,?,?,?,?,?);`
id := dbUtil.Insert(sql, "REDIS", "Redis蜜罐", agent, ipx, country, region, city, info, time.Now().Format("2006-01-02 15:04:05"))
go alert(strconv.FormatInt(id, 10), "new", "REDIS", "Redis蜜罐", agent, ipx, country, region, city, info, time.Now().Format("2006-01-02 15:04:05"))
return id
}
// 更新 Redis 操作
func ReportUpdateRedis(id string, info string) {
sql := `UPDATE hfish_info SET info = info||? WHERE id = ?;`
dbUtil.Update(sql, info, id)
go alert(id, "update", "REDIS", "Redis蜜罐", "", "", "", "", "", info, time.Now().Format("2006-01-02 15:04:05"))
}
// 上报 Mysql
func ReportMysql(ipx string, agent string, info string) int64 {
ipInfo := ip.Get(ipx)
sql := `INSERT INTO hfish_info(type,project_name,agent,ip,ip_info,info,create_time) values(?,?,?,?,?,?,?);`
go alert("MYSQL", agent, ipx, info)
return dbUtil.Insert(sql, "MYSQL", "Mysql蜜罐", agent, ipx, ipInfo, info, time.Now().Format("2006-01-02 15:04:05"))
country, region, city := ip.GetIp(ipx)
sql := `INSERT INTO hfish_info(type,project_name,agent,ip,country,region,city,info,create_time) values(?,?,?,?,?,?,?,?,?);`
id := dbUtil.Insert(sql, "MYSQL", "Mysql蜜罐", agent, ipx, country, region, city, info, time.Now().Format("2006-01-02 15:04:05"))
go alert(strconv.FormatInt(id, 10), "new", "MYSQL", "Mysql蜜罐", agent, ipx, country, region, city, info, time.Now().Format("2006-01-02 15:04:05"))
return id
}
// 更新 Mysql 操作
func ReportUpdateMysql(id string, info string) {
sql := `UPDATE hfish_info SET info = info||? WHERE id = ?;`
dbUtil.Update(sql, info, id)
go alert(id, "update", "MYSQL", "Mysql蜜罐", "", "", "", "", "", info, time.Now().Format("2006-01-02 15:04:05"))
}
// 上报 FTP
func ReportFTP(ipx string, agent string, info string) {
ipInfo := ip.Get(ipx)
sql := `INSERT INTO hfish_info(type,project_name,agent,ip,ip_info,info,create_time) values(?,?,?,?,?,?,?);`
dbUtil.Insert(sql, "FTP", "FTP蜜罐", agent, ipx, ipInfo, info, time.Now().Format("2006-01-02 15:04:05"))
go alert("FTP", agent, ipx, info)
country, region, city := ip.GetIp(ipx)
sql := `INSERT INTO hfish_info(type,project_name,agent,ip,country,region,city,info,create_time) values(?,?,?,?,?,?,?,?,?);`
id := dbUtil.Insert(sql, "FTP", "FTP蜜罐", agent, ipx, country, region, city, info, time.Now().Format("2006-01-02 15:04:05"))
go alert(strconv.FormatInt(id, 10), "new", "FTP", "FTP蜜罐", agent, ipx, country, region, city, info, time.Now().Format("2006-01-02 15:04:05"))
}
// 上报 Telnet
func ReportTelnet(ipx string, agent string, info string) int64 {
ipInfo := ip.Get(ipx)
sql := `INSERT INTO hfish_info(type,project_name,agent,ip,ip_info,info,create_time) values(?,?,?,?,?,?,?);`
go alert("TELNET", agent, ipx, info)
return dbUtil.Insert(sql, "TELNET", "Telnet蜜罐", agent, ipx, ipInfo, info, time.Now().Format("2006-01-02 15:04:05"))
country, region, city := ip.GetIp(ipx)
sql := `INSERT INTO hfish_info(type,project_name,agent,ip,country,region,city,info,create_time) values(?,?,?,?,?,?,?,?,?);`
id := dbUtil.Insert(sql, "TELNET", "Telnet蜜罐", agent, ipx, country, region, city, info, time.Now().Format("2006-01-02 15:04:05"))
go alert(strconv.FormatInt(id, 10), "new", "TELNET", "Telnet蜜罐", agent, ipx, country, region, city, info, time.Now().Format("2006-01-02 15:04:05"))
return id
}
// 更新 Telnet 操作
func ReportUpdateTelnet(id string, info string) {
sql := `UPDATE hfish_info SET info = info||? WHERE id = ?;`
dbUtil.Update(sql, info, id)
go alert(id, "update", "TELNET", "Telnet蜜罐", "", "", "", "", "", info, time.Now().Format("2006-01-02 15:04:05"))
}

View File

@ -1,10 +1,10 @@
package client
import (
"net/rpc"
"HFish/utils/log"
"HFish/utils/conf"
"HFish/utils/ip"
"HFish/core/rpc/core"
"strings"
)
// 上报状态结构
@ -25,26 +25,28 @@ type Result struct {
Id string // 数据库ID更新用 0 为新插入数据
}
func createClient() (*rpc.Client, bool) {
func createClient() (*rpc.Client, string, bool) {
rpcAddr := conf.Get("rpc", "addr")
client, err := rpc.Dial("tcp", rpcAddr)
client, conn, err := rpc.Dial("tcp", rpcAddr)
ipArr := strings.Split(conn.LocalAddr().String(), ":")
if err != nil {
log.Pr("RPC", "127.0.0.1", "连接 RPC Server 失败")
return client, false
return client, "", false
}
return client, true
return client, ipArr[0], true
}
func reportStatus(rpcName string, ftpStatus string, telnetStatus string, httpStatus string, mysqlStatus string, redisStatus string, sshStatus string, webStatus string, darkStatus string) {
client, boolStatus := createClient()
client, addr, boolStatus := createClient()
if boolStatus {
defer client.Close()
status := Status{
ip.GetLocalIp(),
addr,
rpcName,
webStatus,
darkStatus,
@ -69,7 +71,7 @@ func ReportResult(typex string, projectName string, sourceIp string, info string
// projectName 只有 WEB 才需要传项目名 其他协议空即可
// id 0 为 新插入数据,非 0 都是更新数据
// id 非 0 的时候 sourceIp 为空
client, boolStatus := createClient()
client, addr, boolStatus := createClient()
if boolStatus {
defer client.Close()
@ -77,7 +79,7 @@ func ReportResult(typex string, projectName string, sourceIp string, info string
rpcName := conf.Get("rpc", "name")
result := Result{
ip.GetLocalIp(),
addr,
rpcName,
typex,
projectName,

325
core/rpc/core/client.go Normal file
View File

@ -0,0 +1,325 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rpc
import (
"bufio"
"encoding/gob"
"errors"
"io"
"log"
"net"
"net/http"
"sync"
)
// ServerError represents an error that has been returned from
// the remote side of the RPC connection.
type ServerError string
func (e ServerError) Error() string {
return string(e)
}
var ErrShutdown = errors.New("connection is shut down")
// Call represents an active RPC.
type Call struct {
ServiceMethod string // The name of the service and method to call.
Args interface{} // The argument to the function (*struct).
Reply interface{} // The reply from the function (*struct).
Error error // After completion, the error status.
Done chan *Call // Strobes when call is complete.
}
// Client represents an RPC Client.
// There may be multiple outstanding Calls associated
// with a single Client, and a Client may be used by
// multiple goroutines simultaneously.
type Client struct {
codec ClientCodec
reqMutex sync.Mutex // protects following
request Request
mutex sync.Mutex // protects following
seq uint64
pending map[uint64]*Call
closing bool // user has called Close
shutdown bool // server has told us to stop
}
// A ClientCodec implements writing of RPC requests and
// reading of RPC responses for the client side of an RPC session.
// The client calls WriteRequest to write a request to the connection
// and calls ReadResponseHeader and ReadResponseBody in pairs
// to read responses. The client calls Close when finished with the
// connection. ReadResponseBody may be called with a nil
// argument to force the body of the response to be read and then
// discarded.
// See NewClient's comment for information about concurrent access.
type ClientCodec interface {
WriteRequest(*Request, interface{}) error
ReadResponseHeader(*Response) error
ReadResponseBody(interface{}) error
Close() error
}
func (client *Client) send(call *Call) {
client.reqMutex.Lock()
defer client.reqMutex.Unlock()
// Register this call.
client.mutex.Lock()
if client.shutdown || client.closing {
client.mutex.Unlock()
call.Error = ErrShutdown
call.done()
return
}
seq := client.seq
client.seq++
client.pending[seq] = call
client.mutex.Unlock()
// Encode and send the request.
client.request.Seq = seq
client.request.ServiceMethod = call.ServiceMethod
err := client.codec.WriteRequest(&client.request, call.Args)
if err != nil {
client.mutex.Lock()
call = client.pending[seq]
delete(client.pending, seq)
client.mutex.Unlock()
if call != nil {
call.Error = err
call.done()
}
}
}
func (client *Client) input() {
var err error
var response Response
for err == nil {
response = Response{}
err = client.codec.ReadResponseHeader(&response)
if err != nil {
break
}
seq := response.Seq
client.mutex.Lock()
call := client.pending[seq]
delete(client.pending, seq)
client.mutex.Unlock()
switch {
case call == nil:
// We've got no pending call. That usually means that
// WriteRequest partially failed, and call was already
// removed; response is a server telling us about an
// error reading request body. We should still attempt
// to read error body, but there's no one to give it to.
err = client.codec.ReadResponseBody(nil)
if err != nil {
err = errors.New("reading error body: " + err.Error())
}
case response.Error != "":
// We've got an error response. Give this to the request;
// any subsequent requests will get the ReadResponseBody
// error if there is one.
call.Error = ServerError(response.Error)
err = client.codec.ReadResponseBody(nil)
if err != nil {
err = errors.New("reading error body: " + err.Error())
}
call.done()
default:
err = client.codec.ReadResponseBody(call.Reply)
if err != nil {
call.Error = errors.New("reading body " + err.Error())
}
call.done()
}
}
// Terminate pending calls.
client.reqMutex.Lock()
client.mutex.Lock()
client.shutdown = true
closing := client.closing
if err == io.EOF {
if closing {
err = ErrShutdown
} else {
err = io.ErrUnexpectedEOF
}
}
for _, call := range client.pending {
call.Error = err
call.done()
}
client.mutex.Unlock()
client.reqMutex.Unlock()
if debugLog && err != io.EOF && !closing {
log.Println("rpc: client protocol error:", err)
}
}
func (call *Call) done() {
select {
case call.Done <- call:
// ok
default:
// We don't want to block here. It is the caller's responsibility to make
// sure the channel has enough buffer space. See comment in Go().
if debugLog {
log.Println("rpc: discarding Call reply due to insufficient Done chan capacity")
}
}
}
// NewClient returns a new Client to handle requests to the
// set of services at the other end of the connection.
// It adds a buffer to the write side of the connection so
// the header and payload are sent as a unit.
//
// The read and write halves of the connection are serialized independently,
// so no interlocking is required. However each half may be accessed
// concurrently so the implementation of conn should protect against
// concurrent reads or concurrent writes.
func NewClient(conn io.ReadWriteCloser) *Client {
encBuf := bufio.NewWriter(conn)
client := &gobClientCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(encBuf), encBuf}
return NewClientWithCodec(client)
}
// NewClientWithCodec is like NewClient but uses the specified
// codec to encode requests and decode responses.
func NewClientWithCodec(codec ClientCodec) *Client {
client := &Client{
codec: codec,
pending: make(map[uint64]*Call),
}
go client.input()
return client
}
type gobClientCodec struct {
rwc io.ReadWriteCloser
dec *gob.Decoder
enc *gob.Encoder
encBuf *bufio.Writer
}
func (c *gobClientCodec) WriteRequest(r *Request, body interface{}) (err error) {
if err = c.enc.Encode(r); err != nil {
return
}
if err = c.enc.Encode(body); err != nil {
return
}
return c.encBuf.Flush()
}
func (c *gobClientCodec) ReadResponseHeader(r *Response) error {
return c.dec.Decode(r)
}
func (c *gobClientCodec) ReadResponseBody(body interface{}) error {
return c.dec.Decode(body)
}
func (c *gobClientCodec) Close() error {
return c.rwc.Close()
}
// DialHTTP connects to an HTTP RPC server at the specified network address
// listening on the default HTTP RPC path.
func DialHTTP(network, address string) (*Client, error) {
return DialHTTPPath(network, address, DefaultRPCPath)
}
// DialHTTPPath connects to an HTTP RPC server
// at the specified network address and path.
func DialHTTPPath(network, address, path string) (*Client, error) {
var err error
conn, err := net.Dial(network, address)
if err != nil {
return nil, err
}
io.WriteString(conn, "CONNECT "+path+" HTTP/1.0\n\n")
// Require successful HTTP response
// before switching to RPC protocol.
resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "CONNECT"})
if err == nil && resp.Status == connected {
return NewClient(conn), nil
}
if err == nil {
err = errors.New("unexpected HTTP response: " + resp.Status)
}
conn.Close()
return nil, &net.OpError{
Op: "dial-http",
Net: network + " " + address,
Addr: nil,
Err: err,
}
}
// Dial connects to an RPC server at the specified network address.
func Dial(network, address string) (*Client, net.Conn, error) {
conn, err := net.Dial(network, address)
conn.LocalAddr()
if err != nil {
return nil, nil, err
}
return NewClient(conn), conn, nil
}
// Close calls the underlying codec's Close method. If the connection is already
// shutting down, ErrShutdown is returned.
func (client *Client) Close() error {
client.mutex.Lock()
if client.closing {
client.mutex.Unlock()
return ErrShutdown
}
client.closing = true
client.mutex.Unlock()
return client.codec.Close()
}
// Go invokes the function asynchronously. It returns the Call structure representing
// the invocation. The done channel will signal when the call is complete by returning
// the same Call object. If done is nil, Go will allocate a new channel.
// If non-nil, done must be buffered or Go will deliberately crash.
func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call {
call := new(Call)
call.ServiceMethod = serviceMethod
call.Args = args
call.Reply = reply
if done == nil {
done = make(chan *Call, 10) // buffered.
} else {
// If caller passes done != nil, it must arrange that
// done has enough buffer for the number of simultaneous
// RPCs that will be using that channel. If the channel
// is totally unbuffered, it's best not to run at all.
if cap(done) == 0 {
log.Panic("rpc: done channel is unbuffered")
}
}
call.Done = done
client.send(call)
return call
}
// Call invokes the named function, waits for it to complete, and returns its error status.
func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error {
call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done
return call.Error
}

View File

@ -0,0 +1,87 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rpc
import (
"errors"
"fmt"
"net"
"strings"
"testing"
)
type shutdownCodec struct {
responded chan int
closed bool
}
func (c *shutdownCodec) WriteRequest(*Request, interface{}) error { return nil }
func (c *shutdownCodec) ReadResponseBody(interface{}) error { return nil }
func (c *shutdownCodec) ReadResponseHeader(*Response) error {
c.responded <- 1
return errors.New("shutdownCodec ReadResponseHeader")
}
func (c *shutdownCodec) Close() error {
c.closed = true
return nil
}
func TestCloseCodec(t *testing.T) {
codec := &shutdownCodec{responded: make(chan int)}
client := NewClientWithCodec(codec)
<-codec.responded
client.Close()
if !codec.closed {
t.Error("client.Close did not close codec")
}
}
// Test that errors in gob shut down the connection. Issue 7689.
type R struct {
msg []byte // Not exported, so R does not work with gob.
}
type S struct{}
func (s *S) Recv(nul *struct{}, reply *R) error {
*reply = R{[]byte("foo")}
return nil
}
func TestGobError(t *testing.T) {
defer func() {
err := recover()
if err == nil {
t.Fatal("no error")
}
if !strings.Contains("reading body EOF", err.(error).Error()) {
t.Fatal("expected `reading body EOF', got", err)
}
}()
Register(new(S))
listen, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
panic(err)
}
go Accept(listen)
client, err := Dial("tcp", listen.Addr().String())
if err != nil {
panic(err)
}
var reply Reply
err = client.Call("S.Recv", &struct{}{}, &reply)
if err != nil {
panic(err)
}
fmt.Printf("%#v\n", reply)
client.Close()
listen.Close()
}

90
core/rpc/core/debug.go Normal file
View File

@ -0,0 +1,90 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rpc
/*
Some HTML presented at http://machine:port/debug/rpc
Lists services, their methods, and some statistics, still rudimentary.
*/
import (
"fmt"
"html/template"
"net/http"
"sort"
)
const debugText = `<html>
<body>
<title>Services</title>
{{range .}}
<hr>
Service {{.Name}}
<hr>
<table>
<th align=center>Method</th><th align=center>Calls</th>
{{range .Method}}
<tr>
<td align=left font=fixed>{{.Name}}({{.Type.ArgType}}, {{.Type.ReplyType}}) error</td>
<td align=center>{{.Type.NumCalls}}</td>
</tr>
{{end}}
</table>
{{end}}
</body>
</html>`
var debug = template.Must(template.New("RPC debug").Parse(debugText))
// If set, print log statements for internal and I/O errors.
var debugLog = false
type debugMethod struct {
Type *methodType
Name string
}
type methodArray []debugMethod
type debugService struct {
Service *service
Name string
Method methodArray
}
type serviceArray []debugService
func (s serviceArray) Len() int { return len(s) }
func (s serviceArray) Less(i, j int) bool { return s[i].Name < s[j].Name }
func (s serviceArray) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (m methodArray) Len() int { return len(m) }
func (m methodArray) Less(i, j int) bool { return m[i].Name < m[j].Name }
func (m methodArray) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
type debugHTTP struct {
*Server
}
// Runs at /debug/rpc
func (server debugHTTP) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Build a sorted version of the data.
var services serviceArray
server.serviceMap.Range(func(snamei, svci interface{}) bool {
svc := svci.(*service)
ds := debugService{svc, snamei.(string), make(methodArray, 0, len(svc.method))}
for mname, method := range svc.method {
ds.Method = append(ds.Method, debugMethod{method, mname})
}
sort.Sort(ds.Method)
services = append(services, ds)
return true
})
sort.Sort(services)
err := debug.Execute(w, services)
if err != nil {
fmt.Fprintln(w, "rpc: error executing template:", err.Error())
}
}

View File

@ -0,0 +1,354 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package jsonrpc
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/rpc"
"reflect"
"strings"
"testing"
)
type Args struct {
A, B int
}
type Reply struct {
C int
}
type Arith int
type ArithAddResp struct {
Id interface{} `json:"id"`
Result Reply `json:"result"`
Error interface{} `json:"error"`
}
func (t *Arith) Add(args *Args, reply *Reply) error {
reply.C = args.A + args.B
return nil
}
func (t *Arith) Mul(args *Args, reply *Reply) error {
reply.C = args.A * args.B
return nil
}
func (t *Arith) Div(args *Args, reply *Reply) error {
if args.B == 0 {
return errors.New("divide by zero")
}
reply.C = args.A / args.B
return nil
}
func (t *Arith) Error(args *Args, reply *Reply) error {
panic("ERROR")
}
type BuiltinTypes struct{}
func (BuiltinTypes) Map(i int, reply *map[int]int) error {
(*reply)[i] = i
return nil
}
func (BuiltinTypes) Slice(i int, reply *[]int) error {
*reply = append(*reply, i)
return nil
}
func (BuiltinTypes) Array(i int, reply *[1]int) error {
(*reply)[0] = i
return nil
}
func init() {
rpc.Register(new(Arith))
rpc.Register(BuiltinTypes{})
}
func TestServerNoParams(t *testing.T) {
cli, srv := net.Pipe()
defer cli.Close()
go ServeConn(srv)
dec := json.NewDecoder(cli)
fmt.Fprintf(cli, `{"method": "Arith.Add", "id": "123"}`)
var resp ArithAddResp
if err := dec.Decode(&resp); err != nil {
t.Fatalf("Decode after no params: %s", err)
}
if resp.Error == nil {
t.Fatalf("Expected error, got nil")
}
}
func TestServerEmptyMessage(t *testing.T) {
cli, srv := net.Pipe()
defer cli.Close()
go ServeConn(srv)
dec := json.NewDecoder(cli)
fmt.Fprintf(cli, "{}")
var resp ArithAddResp
if err := dec.Decode(&resp); err != nil {
t.Fatalf("Decode after empty: %s", err)
}
if resp.Error == nil {
t.Fatalf("Expected error, got nil")
}
}
func TestServer(t *testing.T) {
cli, srv := net.Pipe()
defer cli.Close()
go ServeConn(srv)
dec := json.NewDecoder(cli)
// Send hand-coded requests to server, parse responses.
for i := 0; i < 10; i++ {
fmt.Fprintf(cli, `{"method": "Arith.Add", "id": "\u%04d", "params": [{"A": %d, "B": %d}]}`, i, i, i+1)
var resp ArithAddResp
err := dec.Decode(&resp)
if err != nil {
t.Fatalf("Decode: %s", err)
}
if resp.Error != nil {
t.Fatalf("resp.Error: %s", resp.Error)
}
if resp.Id.(string) != string(i) {
t.Fatalf("resp: bad id %q want %q", resp.Id.(string), string(i))
}
if resp.Result.C != 2*i+1 {
t.Fatalf("resp: bad result: %d+%d=%d", i, i+1, resp.Result.C)
}
}
}
func TestClient(t *testing.T) {
// Assume server is okay (TestServer is above).
// Test client against server.
cli, srv := net.Pipe()
go ServeConn(srv)
client := NewClient(cli)
defer client.Close()
// Synchronous calls
args := &Args{7, 8}
reply := new(Reply)
err := client.Call("Arith.Add", args, reply)
if err != nil {
t.Errorf("Add: expected no error but got string %q", err.Error())
}
if reply.C != args.A+args.B {
t.Errorf("Add: got %d expected %d", reply.C, args.A+args.B)
}
args = &Args{7, 8}
reply = new(Reply)
err = client.Call("Arith.Mul", args, reply)
if err != nil {
t.Errorf("Mul: expected no error but got string %q", err.Error())
}
if reply.C != args.A*args.B {
t.Errorf("Mul: got %d expected %d", reply.C, args.A*args.B)
}
// Out of order.
args = &Args{7, 8}
mulReply := new(Reply)
mulCall := client.Go("Arith.Mul", args, mulReply, nil)
addReply := new(Reply)
addCall := client.Go("Arith.Add", args, addReply, nil)
addCall = <-addCall.Done
if addCall.Error != nil {
t.Errorf("Add: expected no error but got string %q", addCall.Error.Error())
}
if addReply.C != args.A+args.B {
t.Errorf("Add: got %d expected %d", addReply.C, args.A+args.B)
}
mulCall = <-mulCall.Done
if mulCall.Error != nil {
t.Errorf("Mul: expected no error but got string %q", mulCall.Error.Error())
}
if mulReply.C != args.A*args.B {
t.Errorf("Mul: got %d expected %d", mulReply.C, args.A*args.B)
}
// Error test
args = &Args{7, 0}
reply = new(Reply)
err = client.Call("Arith.Div", args, reply)
// expect an error: zero divide
if err == nil {
t.Error("Div: expected error")
} else if err.Error() != "divide by zero" {
t.Error("Div: expected divide by zero error; got", err)
}
}
func TestBuiltinTypes(t *testing.T) {
cli, srv := net.Pipe()
go ServeConn(srv)
client := NewClient(cli)
defer client.Close()
// Map
arg := 7
replyMap := map[int]int{}
err := client.Call("BuiltinTypes.Map", arg, &replyMap)
if err != nil {
t.Errorf("Map: expected no error but got string %q", err.Error())
}
if replyMap[arg] != arg {
t.Errorf("Map: expected %d got %d", arg, replyMap[arg])
}
// Slice
replySlice := []int{}
err = client.Call("BuiltinTypes.Slice", arg, &replySlice)
if err != nil {
t.Errorf("Slice: expected no error but got string %q", err.Error())
}
if e := []int{arg}; !reflect.DeepEqual(replySlice, e) {
t.Errorf("Slice: expected %v got %v", e, replySlice)
}
// Array
replyArray := [1]int{}
err = client.Call("BuiltinTypes.Array", arg, &replyArray)
if err != nil {
t.Errorf("Array: expected no error but got string %q", err.Error())
}
if e := [1]int{arg}; !reflect.DeepEqual(replyArray, e) {
t.Errorf("Array: expected %v got %v", e, replyArray)
}
}
func TestMalformedInput(t *testing.T) {
cli, srv := net.Pipe()
go cli.Write([]byte(`{id:1}`)) // invalid json
ServeConn(srv) // must return, not loop
}
func TestMalformedOutput(t *testing.T) {
cli, srv := net.Pipe()
go srv.Write([]byte(`{"id":0,"result":null,"error":null}`))
go ioutil.ReadAll(srv)
client := NewClient(cli)
defer client.Close()
args := &Args{7, 8}
reply := new(Reply)
err := client.Call("Arith.Add", args, reply)
if err == nil {
t.Error("expected error")
}
}
func TestServerErrorHasNullResult(t *testing.T) {
var out bytes.Buffer
sc := NewServerCodec(struct {
io.Reader
io.Writer
io.Closer
}{
Reader: strings.NewReader(`{"method": "Arith.Add", "id": "123", "params": []}`),
Writer: &out,
Closer: ioutil.NopCloser(nil),
})
r := new(rpc.Request)
if err := sc.ReadRequestHeader(r); err != nil {
t.Fatal(err)
}
const valueText = "the value we don't want to see"
const errorText = "some error"
err := sc.WriteResponse(&rpc.Response{
ServiceMethod: "Method",
Seq: 1,
Error: errorText,
}, valueText)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(out.String(), errorText) {
t.Fatalf("Response didn't contain expected error %q: %s", errorText, &out)
}
if strings.Contains(out.String(), valueText) {
t.Errorf("Response contains both an error and value: %s", &out)
}
}
func TestUnexpectedError(t *testing.T) {
cli, srv := myPipe()
go cli.PipeWriter.CloseWithError(errors.New("unexpected error!")) // reader will get this error
ServeConn(srv) // must return, not loop
}
// Copied from package net.
func myPipe() (*pipe, *pipe) {
r1, w1 := io.Pipe()
r2, w2 := io.Pipe()
return &pipe{r1, w2}, &pipe{r2, w1}
}
type pipe struct {
*io.PipeReader
*io.PipeWriter
}
type pipeAddr int
func (pipeAddr) Network() string {
return "pipe"
}
func (pipeAddr) String() string {
return "pipe"
}
func (p *pipe) Close() error {
err := p.PipeReader.Close()
err1 := p.PipeWriter.Close()
if err == nil {
err = err1
}
return err
}
func (p *pipe) LocalAddr() net.Addr {
return pipeAddr(0)
}
func (p *pipe) RemoteAddr() net.Addr {
return pipeAddr(0)
}
func (p *pipe) SetTimeout(nsec int64) error {
return errors.New("net.Pipe does not support timeouts")
}
func (p *pipe) SetReadTimeout(nsec int64) error {
return errors.New("net.Pipe does not support timeouts")
}
func (p *pipe) SetWriteTimeout(nsec int64) error {
return errors.New("net.Pipe does not support timeouts")
}

View File

@ -0,0 +1,124 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package jsonrpc implements a JSON-RPC 1.0 ClientCodec and ServerCodec
// for the rpc package.
// For JSON-RPC 2.0 support, see https://godoc.org/?q=json-rpc+2.0
package jsonrpc
import (
"encoding/json"
"fmt"
"io"
"net"
"net/rpc"
"sync"
)
type clientCodec struct {
dec *json.Decoder // for reading JSON values
enc *json.Encoder // for writing JSON values
c io.Closer
// temporary work space
req clientRequest
resp clientResponse
// JSON-RPC responses include the request id but not the request method.
// Package rpc expects both.
// We save the request method in pending when sending a request
// and then look it up by request ID when filling out the rpc Response.
mutex sync.Mutex // protects pending
pending map[uint64]string // map request id to method name
}
// NewClientCodec returns a new rpc.ClientCodec using JSON-RPC on conn.
func NewClientCodec(conn io.ReadWriteCloser) rpc.ClientCodec {
return &clientCodec{
dec: json.NewDecoder(conn),
enc: json.NewEncoder(conn),
c: conn,
pending: make(map[uint64]string),
}
}
type clientRequest struct {
Method string `json:"method"`
Params [1]interface{} `json:"params"`
Id uint64 `json:"id"`
}
func (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) error {
c.mutex.Lock()
c.pending[r.Seq] = r.ServiceMethod
c.mutex.Unlock()
c.req.Method = r.ServiceMethod
c.req.Params[0] = param
c.req.Id = r.Seq
return c.enc.Encode(&c.req)
}
type clientResponse struct {
Id uint64 `json:"id"`
Result *json.RawMessage `json:"result"`
Error interface{} `json:"error"`
}
func (r *clientResponse) reset() {
r.Id = 0
r.Result = nil
r.Error = nil
}
func (c *clientCodec) ReadResponseHeader(r *rpc.Response) error {
c.resp.reset()
if err := c.dec.Decode(&c.resp); err != nil {
return err
}
c.mutex.Lock()
r.ServiceMethod = c.pending[c.resp.Id]
delete(c.pending, c.resp.Id)
c.mutex.Unlock()
r.Error = ""
r.Seq = c.resp.Id
if c.resp.Error != nil || c.resp.Result == nil {
x, ok := c.resp.Error.(string)
if !ok {
return fmt.Errorf("invalid error %v", c.resp.Error)
}
if x == "" {
x = "unspecified error"
}
r.Error = x
}
return nil
}
func (c *clientCodec) ReadResponseBody(x interface{}) error {
if x == nil {
return nil
}
return json.Unmarshal(*c.resp.Result, x)
}
func (c *clientCodec) Close() error {
return c.c.Close()
}
// NewClient returns a new rpc.Client to handle requests to the
// set of services at the other end of the connection.
func NewClient(conn io.ReadWriteCloser) *rpc.Client {
return rpc.NewClientWithCodec(NewClientCodec(conn))
}
// Dial connects to a JSON-RPC server at the specified network address.
func Dial(network, address string) (*rpc.Client, error) {
conn, err := net.Dial(network, address)
if err != nil {
return nil, err
}
return NewClient(conn), err
}

View File

@ -0,0 +1,134 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package jsonrpc
import (
"encoding/json"
"errors"
"io"
"net/rpc"
"sync"
)
var errMissingParams = errors.New("jsonrpc: request body missing params")
type serverCodec struct {
dec *json.Decoder // for reading JSON values
enc *json.Encoder // for writing JSON values
c io.Closer
// temporary work space
req serverRequest
// JSON-RPC clients can use arbitrary json values as request IDs.
// Package rpc expects uint64 request IDs.
// We assign uint64 sequence numbers to incoming requests
// but save the original request ID in the pending map.
// When rpc responds, we use the sequence number in
// the response to find the original request ID.
mutex sync.Mutex // protects seq, pending
seq uint64
pending map[uint64]*json.RawMessage
}
// NewServerCodec returns a new rpc.ServerCodec using JSON-RPC on conn.
func NewServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec {
return &serverCodec{
dec: json.NewDecoder(conn),
enc: json.NewEncoder(conn),
c: conn,
pending: make(map[uint64]*json.RawMessage),
}
}
type serverRequest struct {
Method string `json:"method"`
Params *json.RawMessage `json:"params"`
Id *json.RawMessage `json:"id"`
}
func (r *serverRequest) reset() {
r.Method = ""
r.Params = nil
r.Id = nil
}
type serverResponse struct {
Id *json.RawMessage `json:"id"`
Result interface{} `json:"result"`
Error interface{} `json:"error"`
}
func (c *serverCodec) ReadRequestHeader(r *rpc.Request) error {
c.req.reset()
if err := c.dec.Decode(&c.req); err != nil {
return err
}
r.ServiceMethod = c.req.Method
// JSON request id can be any JSON value;
// RPC package expects uint64. Translate to
// internal uint64 and save JSON on the side.
c.mutex.Lock()
c.seq++
c.pending[c.seq] = c.req.Id
c.req.Id = nil
r.Seq = c.seq
c.mutex.Unlock()
return nil
}
func (c *serverCodec) ReadRequestBody(x interface{}) error {
if x == nil {
return nil
}
if c.req.Params == nil {
return errMissingParams
}
// JSON params is array value.
// RPC params is struct.
// Unmarshal into array containing struct for now.
// Should think about making RPC more general.
var params [1]interface{}
params[0] = x
return json.Unmarshal(*c.req.Params, &params)
}
var null = json.RawMessage([]byte("null"))
func (c *serverCodec) WriteResponse(r *rpc.Response, x interface{}) error {
c.mutex.Lock()
b, ok := c.pending[r.Seq]
if !ok {
c.mutex.Unlock()
return errors.New("invalid sequence number in response")
}
delete(c.pending, r.Seq)
c.mutex.Unlock()
if b == nil {
// Invalid request so no id. Use JSON null.
b = &null
}
resp := serverResponse{Id: b}
if r.Error == "" {
resp.Result = x
} else {
resp.Error = r.Error
}
return c.enc.Encode(resp)
}
func (c *serverCodec) Close() error {
return c.c.Close()
}
// ServeConn runs the JSON-RPC server on a single connection.
// ServeConn blocks, serving the connection until the client hangs up.
// The caller typically invokes ServeConn in a go statement.
func ServeConn(conn io.ReadWriteCloser) {
rpc.ServeCodec(NewServerCodec(conn))
}

727
core/rpc/core/server.go Normal file
View File

@ -0,0 +1,727 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package rpc provides access to the exported methods of an object across a
network or other I/O connection. A server registers an object, making it visible
as a service with the name of the type of the object. After registration, exported
methods of the object will be accessible remotely. A server may register multiple
objects (services) of different types but it is an error to register multiple
objects of the same type.
Only methods that satisfy these criteria will be made available for remote access;
other methods will be ignored:
- the method's type is exported.
- the method is exported.
- the method has two arguments, both exported (or builtin) types.
- the method's second argument is a pointer.
- the method has return type error.
In effect, the method must look schematically like
func (t *T) MethodName(argType T1, replyType *T2) error
where T1 and T2 can be marshaled by encoding/gob.
These requirements apply even if a different codec is used.
(In the future, these requirements may soften for custom codecs.)
The method's first argument represents the arguments provided by the caller; the
second argument represents the result parameters to be returned to the caller.
The method's return value, if non-nil, is passed back as a string that the client
sees as if created by errors.New. If an error is returned, the reply parameter
will not be sent back to the client.
The server may handle requests on a single connection by calling ServeConn. More
typically it will create a network listener and call Accept or, for an HTTP
listener, HandleHTTP and http.Serve.
A client wishing to use the service establishes a connection and then invokes
NewClient on the connection. The convenience function Dial (DialHTTP) performs
both steps for a raw network connection (an HTTP connection). The resulting
Client object has two methods, Call and Go, that specify the service and method to
call, a pointer containing the arguments, and a pointer to receive the result
parameters.
The Call method waits for the remote call to complete while the Go method
launches the call asynchronously and signals completion using the Call
structure's Done channel.
Unless an explicit codec is set up, package encoding/gob is used to
transport the data.
Here is a simple example. A server wishes to export an object of type Arith:
package server
import "errors"
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
func (t *Arith) Divide(args *Args, quo *Quotient) error {
if args.B == 0 {
return errors.New("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
}
The server calls (for HTTP service):
arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":1234")
if e != nil {
log.Fatal("listen error:", e)
}
go http.Serve(l, nil)
At this point, clients can see a service "Arith" with methods "Arith.Multiply" and
"Arith.Divide". To invoke one, a client first dials the server:
client, err := rpc.DialHTTP("tcp", serverAddress + ":1234")
if err != nil {
log.Fatal("dialing:", err)
}
Then it can make a remote call:
// Synchronous call
args := &server.Args{7,8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d", args.A, args.B, reply)
or
// Asynchronous call
quotient := new(Quotient)
divCall := client.Go("Arith.Divide", args, quotient, nil)
replyCall := <-divCall.Done // will be equal to divCall
// check errors, print, etc.
A server implementation will often provide a simple, type-safe wrapper for the
client.
The net/rpc package is frozen and is not accepting new features.
*/
package rpc
import (
"bufio"
"encoding/gob"
"errors"
"io"
"log"
"net"
"net/http"
"reflect"
"strings"
"sync"
"unicode"
"unicode/utf8"
)
const (
// Defaults used by HandleHTTP
DefaultRPCPath = "/_goRPC_"
DefaultDebugPath = "/debug/rpc"
)
// Precompute the reflect type for error. Can't use error directly
// because Typeof takes an empty interface value. This is annoying.
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
type methodType struct {
sync.Mutex // protects counters
method reflect.Method
ArgType reflect.Type
ReplyType reflect.Type
numCalls uint
}
type service struct {
name string // name of service
rcvr reflect.Value // receiver of methods for the service
typ reflect.Type // type of the receiver
method map[string]*methodType // registered methods
}
// Request is a header written before every RPC call. It is used internally
// but documented here as an aid to debugging, such as when analyzing
// network traffic.
type Request struct {
ServiceMethod string // format: "Service.Method"
Seq uint64 // sequence number chosen by client
next *Request // for free list in Server
}
// Response is a header written before every RPC return. It is used internally
// but documented here as an aid to debugging, such as when analyzing
// network traffic.
type Response struct {
ServiceMethod string // echoes that of the Request
Seq uint64 // echoes that of the request
Error string // error, if any.
next *Response // for free list in Server
}
// Server represents an RPC Server.
type Server struct {
serviceMap sync.Map // map[string]*service
reqLock sync.Mutex // protects freeReq
freeReq *Request
respLock sync.Mutex // protects freeResp
freeResp *Response
}
// NewServer returns a new Server.
func NewServer() *Server {
return &Server{}
}
// DefaultServer is the default instance of *Server.
var DefaultServer = NewServer()
// Is this an exported - upper case - name?
func isExported(name string) bool {
rune, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(rune)
}
// Is this type exported or a builtin?
func isExportedOrBuiltinType(t reflect.Type) bool {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
// PkgPath will be non-empty even for an exported type,
// so we need to check the type name as well.
return isExported(t.Name()) || t.PkgPath() == ""
}
// Register publishes in the server the set of methods of the
// receiver value that satisfy the following conditions:
// - exported method of exported type
// - two arguments, both of exported type
// - the second argument is a pointer
// - one return value, of type error
// It returns an error if the receiver is not an exported type or has
// no suitable methods. It also logs the error using package log.
// The client accesses each method using a string of the form "Type.Method",
// where Type is the receiver's concrete type.
func (server *Server) Register(rcvr interface{}) error {
return server.register(rcvr, "", false)
}
// RegisterName is like Register but uses the provided name for the type
// instead of the receiver's concrete type.
func (server *Server) RegisterName(name string, rcvr interface{}) error {
return server.register(rcvr, name, true)
}
func (server *Server) register(rcvr interface{}, name string, useName bool) error {
s := new(service)
s.typ = reflect.TypeOf(rcvr)
s.rcvr = reflect.ValueOf(rcvr)
sname := reflect.Indirect(s.rcvr).Type().Name()
if useName {
sname = name
}
if sname == "" {
s := "rpc.Register: no service name for type " + s.typ.String()
log.Print(s)
return errors.New(s)
}
if !isExported(sname) && !useName {
s := "rpc.Register: type " + sname + " is not exported"
log.Print(s)
return errors.New(s)
}
s.name = sname
// Install the methods
s.method = suitableMethods(s.typ, true)
if len(s.method) == 0 {
str := ""
// To help the user, see if a pointer receiver would work.
method := suitableMethods(reflect.PtrTo(s.typ), false)
if len(method) != 0 {
str = "rpc.Register: type " + sname + " has no exported methods of suitable type (hint: pass a pointer to value of that type)"
} else {
str = "rpc.Register: type " + sname + " has no exported methods of suitable type"
}
log.Print(str)
return errors.New(str)
}
if _, dup := server.serviceMap.LoadOrStore(sname, s); dup {
return errors.New("rpc: service already defined: " + sname)
}
return nil
}
// suitableMethods returns suitable Rpc methods of typ, it will report
// error using log if reportErr is true.
func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType {
methods := make(map[string]*methodType)
for m := 0; m < typ.NumMethod(); m++ {
method := typ.Method(m)
mtype := method.Type
mname := method.Name
// Method must be exported.
if method.PkgPath != "" {
continue
}
// Method needs three ins: receiver, *args, *reply.
if mtype.NumIn() != 3 {
if reportErr {
log.Printf("rpc.Register: method %q has %d input parameters; needs exactly three\n", mname, mtype.NumIn())
}
continue
}
// First arg need not be a pointer.
argType := mtype.In(1)
if !isExportedOrBuiltinType(argType) {
if reportErr {
log.Printf("rpc.Register: argument type of method %q is not exported: %q\n", mname, argType)
}
continue
}
// Second arg must be a pointer.
replyType := mtype.In(2)
if replyType.Kind() != reflect.Ptr {
if reportErr {
log.Printf("rpc.Register: reply type of method %q is not a pointer: %q\n", mname, replyType)
}
continue
}
// Reply type must be exported.
if !isExportedOrBuiltinType(replyType) {
if reportErr {
log.Printf("rpc.Register: reply type of method %q is not exported: %q\n", mname, replyType)
}
continue
}
// Method needs one out.
if mtype.NumOut() != 1 {
if reportErr {
log.Printf("rpc.Register: method %q has %d output parameters; needs exactly one\n", mname, mtype.NumOut())
}
continue
}
// The return type of the method must be error.
if returnType := mtype.Out(0); returnType != typeOfError {
if reportErr {
log.Printf("rpc.Register: return type of method %q is %q, must be error\n", mname, returnType)
}
continue
}
methods[mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType}
}
return methods
}
// A value sent as a placeholder for the server's response value when the server
// receives an invalid request. It is never decoded by the client since the Response
// contains an error when it is used.
var invalidRequest = struct{}{}
func (server *Server) sendResponse(sending *sync.Mutex, req *Request, reply interface{}, codec ServerCodec, errmsg string) {
resp := server.getResponse()
// Encode the response header
resp.ServiceMethod = req.ServiceMethod
if errmsg != "" {
resp.Error = errmsg
reply = invalidRequest
}
resp.Seq = req.Seq
sending.Lock()
err := codec.WriteResponse(resp, reply)
if debugLog && err != nil {
log.Println("rpc: writing response:", err)
}
sending.Unlock()
server.freeResponse(resp)
}
func (m *methodType) NumCalls() (n uint) {
m.Lock()
n = m.numCalls
m.Unlock()
return n
}
func (s *service) call(server *Server, sending *sync.Mutex, wg *sync.WaitGroup, mtype *methodType, req *Request, argv, replyv reflect.Value, codec ServerCodec) {
if wg != nil {
defer wg.Done()
}
mtype.Lock()
mtype.numCalls++
mtype.Unlock()
function := mtype.method.Func
// Invoke the method, providing a new value for the reply.
returnValues := function.Call([]reflect.Value{s.rcvr, argv, replyv})
// The return value for the method is an error.
errInter := returnValues[0].Interface()
errmsg := ""
if errInter != nil {
errmsg = errInter.(error).Error()
}
server.sendResponse(sending, req, replyv.Interface(), codec, errmsg)
server.freeRequest(req)
}
type gobServerCodec struct {
rwc io.ReadWriteCloser
dec *gob.Decoder
enc *gob.Encoder
encBuf *bufio.Writer
closed bool
}
func (c *gobServerCodec) ReadRequestHeader(r *Request) error {
return c.dec.Decode(r)
}
func (c *gobServerCodec) ReadRequestBody(body interface{}) error {
return c.dec.Decode(body)
}
func (c *gobServerCodec) WriteResponse(r *Response, body interface{}) (err error) {
if err = c.enc.Encode(r); err != nil {
if c.encBuf.Flush() == nil {
// Gob couldn't encode the header. Should not happen, so if it does,
// shut down the connection to signal that the connection is broken.
log.Println("rpc: gob error encoding response:", err)
c.Close()
}
return
}
if err = c.enc.Encode(body); err != nil {
if c.encBuf.Flush() == nil {
// Was a gob problem encoding the body but the header has been written.
// Shut down the connection to signal that the connection is broken.
log.Println("rpc: gob error encoding body:", err)
c.Close()
}
return
}
return c.encBuf.Flush()
}
func (c *gobServerCodec) Close() error {
if c.closed {
// Only call c.rwc.Close once; otherwise the semantics are undefined.
return nil
}
c.closed = true
return c.rwc.Close()
}
// ServeConn runs the server on a single connection.
// ServeConn blocks, serving the connection until the client hangs up.
// The caller typically invokes ServeConn in a go statement.
// ServeConn uses the gob wire format (see package gob) on the
// connection. To use an alternate codec, use ServeCodec.
// See NewClient's comment for information about concurrent access.
func (server *Server) ServeConn(conn io.ReadWriteCloser) {
buf := bufio.NewWriter(conn)
srv := &gobServerCodec{
rwc: conn,
dec: gob.NewDecoder(conn),
enc: gob.NewEncoder(buf),
encBuf: buf,
}
server.ServeCodec(srv)
}
// ServeCodec is like ServeConn but uses the specified codec to
// decode requests and encode responses.
func (server *Server) ServeCodec(codec ServerCodec) {
sending := new(sync.Mutex)
wg := new(sync.WaitGroup)
for {
service, mtype, req, argv, replyv, keepReading, err := server.readRequest(codec)
if err != nil {
if debugLog && err != io.EOF {
log.Println("rpc:", err)
}
if !keepReading {
break
}
// send a response if we actually managed to read a header.
if req != nil {
server.sendResponse(sending, req, invalidRequest, codec, err.Error())
server.freeRequest(req)
}
continue
}
wg.Add(1)
go service.call(server, sending, wg, mtype, req, argv, replyv, codec)
}
// We've seen that there are no more requests.
// Wait for responses to be sent before closing codec.
wg.Wait()
codec.Close()
}
// ServeRequest is like ServeCodec but synchronously serves a single request.
// It does not close the codec upon completion.
func (server *Server) ServeRequest(codec ServerCodec) error {
sending := new(sync.Mutex)
service, mtype, req, argv, replyv, keepReading, err := server.readRequest(codec)
if err != nil {
if !keepReading {
return err
}
// send a response if we actually managed to read a header.
if req != nil {
server.sendResponse(sending, req, invalidRequest, codec, err.Error())
server.freeRequest(req)
}
return err
}
service.call(server, sending, nil, mtype, req, argv, replyv, codec)
return nil
}
func (server *Server) getRequest() *Request {
server.reqLock.Lock()
req := server.freeReq
if req == nil {
req = new(Request)
} else {
server.freeReq = req.next
*req = Request{}
}
server.reqLock.Unlock()
return req
}
func (server *Server) freeRequest(req *Request) {
server.reqLock.Lock()
req.next = server.freeReq
server.freeReq = req
server.reqLock.Unlock()
}
func (server *Server) getResponse() *Response {
server.respLock.Lock()
resp := server.freeResp
if resp == nil {
resp = new(Response)
} else {
server.freeResp = resp.next
*resp = Response{}
}
server.respLock.Unlock()
return resp
}
func (server *Server) freeResponse(resp *Response) {
server.respLock.Lock()
resp.next = server.freeResp
server.freeResp = resp
server.respLock.Unlock()
}
func (server *Server) readRequest(codec ServerCodec) (service *service, mtype *methodType, req *Request, argv, replyv reflect.Value, keepReading bool, err error) {
service, mtype, req, keepReading, err = server.readRequestHeader(codec)
if err != nil {
if !keepReading {
return
}
// discard body
codec.ReadRequestBody(nil)
return
}
// Decode the argument value.
argIsValue := false // if true, need to indirect before calling.
if mtype.ArgType.Kind() == reflect.Ptr {
argv = reflect.New(mtype.ArgType.Elem())
} else {
argv = reflect.New(mtype.ArgType)
argIsValue = true
}
// argv guaranteed to be a pointer now.
if err = codec.ReadRequestBody(argv.Interface()); err != nil {
return
}
if argIsValue {
argv = argv.Elem()
}
replyv = reflect.New(mtype.ReplyType.Elem())
switch mtype.ReplyType.Elem().Kind() {
case reflect.Map:
replyv.Elem().Set(reflect.MakeMap(mtype.ReplyType.Elem()))
case reflect.Slice:
replyv.Elem().Set(reflect.MakeSlice(mtype.ReplyType.Elem(), 0, 0))
}
return
}
func (server *Server) readRequestHeader(codec ServerCodec) (svc *service, mtype *methodType, req *Request, keepReading bool, err error) {
// Grab the request header.
req = server.getRequest()
err = codec.ReadRequestHeader(req)
if err != nil {
req = nil
if err == io.EOF || err == io.ErrUnexpectedEOF {
return
}
err = errors.New("rpc: server cannot decode request: " + err.Error())
return
}
// We read the header successfully. If we see an error now,
// we can still recover and move on to the next request.
keepReading = true
dot := strings.LastIndex(req.ServiceMethod, ".")
if dot < 0 {
err = errors.New("rpc: service/method request ill-formed: " + req.ServiceMethod)
return
}
serviceName := req.ServiceMethod[:dot]
methodName := req.ServiceMethod[dot+1:]
// Look up the request.
svci, ok := server.serviceMap.Load(serviceName)
if !ok {
err = errors.New("rpc: can't find service " + req.ServiceMethod)
return
}
svc = svci.(*service)
mtype = svc.method[methodName]
if mtype == nil {
err = errors.New("rpc: can't find method " + req.ServiceMethod)
}
return
}
// Accept accepts connections on the listener and serves requests
// for each incoming connection. Accept blocks until the listener
// returns a non-nil error. The caller typically invokes Accept in a
// go statement.
func (server *Server) Accept(lis net.Listener) {
for {
conn, err := lis.Accept()
if err != nil {
log.Print("rpc.Serve: accept:", err.Error())
return
}
go server.ServeConn(conn)
}
}
// Register publishes the receiver's methods in the DefaultServer.
func Register(rcvr interface{}) error { return DefaultServer.Register(rcvr) }
// RegisterName is like Register but uses the provided name for the type
// instead of the receiver's concrete type.
func RegisterName(name string, rcvr interface{}) error {
return DefaultServer.RegisterName(name, rcvr)
}
// A ServerCodec implements reading of RPC requests and writing of
// RPC responses for the server side of an RPC session.
// The server calls ReadRequestHeader and ReadRequestBody in pairs
// to read requests from the connection, and it calls WriteResponse to
// write a response back. The server calls Close when finished with the
// connection. ReadRequestBody may be called with a nil
// argument to force the body of the request to be read and discarded.
// See NewClient's comment for information about concurrent access.
type ServerCodec interface {
ReadRequestHeader(*Request) error
ReadRequestBody(interface{}) error
WriteResponse(*Response, interface{}) error
// Close can be called multiple times and must be idempotent.
Close() error
}
// ServeConn runs the DefaultServer on a single connection.
// ServeConn blocks, serving the connection until the client hangs up.
// The caller typically invokes ServeConn in a go statement.
// ServeConn uses the gob wire format (see package gob) on the
// connection. To use an alternate codec, use ServeCodec.
// See NewClient's comment for information about concurrent access.
func ServeConn(conn io.ReadWriteCloser) {
DefaultServer.ServeConn(conn)
}
// ServeCodec is like ServeConn but uses the specified codec to
// decode requests and encode responses.
func ServeCodec(codec ServerCodec) {
DefaultServer.ServeCodec(codec)
}
// ServeRequest is like ServeCodec but synchronously serves a single request.
// It does not close the codec upon completion.
func ServeRequest(codec ServerCodec) error {
return DefaultServer.ServeRequest(codec)
}
// Accept accepts connections on the listener and serves requests
// to DefaultServer for each incoming connection.
// Accept blocks; the caller typically invokes it in a go statement.
func Accept(lis net.Listener) { DefaultServer.Accept(lis) }
// Can connect to RPC service using HTTP CONNECT to rpcPath.
var connected = "200 Connected to Go RPC"
// ServeHTTP implements an http.Handler that answers RPC requests.
func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if req.Method != "CONNECT" {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusMethodNotAllowed)
io.WriteString(w, "405 must CONNECT\n")
return
}
conn, _, err := w.(http.Hijacker).Hijack()
if err != nil {
log.Print("rpc hijacking ", req.RemoteAddr, ": ", err.Error())
return
}
io.WriteString(conn, "HTTP/1.0 "+connected+"\n\n")
server.ServeConn(conn)
}
// HandleHTTP registers an HTTP handler for RPC messages on rpcPath,
// and a debugging handler on debugPath.
// It is still necessary to invoke http.Serve(), typically in a go statement.
func (server *Server) HandleHTTP(rpcPath, debugPath string) {
http.Handle(rpcPath, server)
http.Handle(debugPath, debugHTTP{server})
}
// HandleHTTP registers an HTTP handler for RPC messages to DefaultServer
// on DefaultRPCPath and a debugging handler on DefaultDebugPath.
// It is still necessary to invoke http.Serve(), typically in a go statement.
func HandleHTTP() {
DefaultServer.HandleHTTP(DefaultRPCPath, DefaultDebugPath)
}

View File

@ -0,0 +1,839 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rpc
import (
"errors"
"fmt"
"io"
"log"
"net"
"net/http/httptest"
"reflect"
"runtime"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
)
var (
newServer *Server
serverAddr, newServerAddr string
httpServerAddr string
once, newOnce, httpOnce sync.Once
)
const (
newHttpPath = "/foo"
)
type Args struct {
A, B int
}
type Reply struct {
C int
}
type Arith int
// Some of Arith's methods have value args, some have pointer args. That's deliberate.
func (t *Arith) Add(args Args, reply *Reply) error {
reply.C = args.A + args.B
return nil
}
func (t *Arith) Mul(args *Args, reply *Reply) error {
reply.C = args.A * args.B
return nil
}
func (t *Arith) Div(args Args, reply *Reply) error {
if args.B == 0 {
return errors.New("divide by zero")
}
reply.C = args.A / args.B
return nil
}
func (t *Arith) String(args *Args, reply *string) error {
*reply = fmt.Sprintf("%d+%d=%d", args.A, args.B, args.A+args.B)
return nil
}
func (t *Arith) Scan(args string, reply *Reply) (err error) {
_, err = fmt.Sscan(args, &reply.C)
return
}
func (t *Arith) Error(args *Args, reply *Reply) error {
panic("ERROR")
}
func (t *Arith) SleepMilli(args *Args, reply *Reply) error {
time.Sleep(time.Duration(args.A) * time.Millisecond)
return nil
}
type hidden int
func (t *hidden) Exported(args Args, reply *Reply) error {
reply.C = args.A + args.B
return nil
}
type Embed struct {
hidden
}
type BuiltinTypes struct{}
func (BuiltinTypes) Map(args *Args, reply *map[int]int) error {
(*reply)[args.A] = args.B
return nil
}
func (BuiltinTypes) Slice(args *Args, reply *[]int) error {
*reply = append(*reply, args.A, args.B)
return nil
}
func (BuiltinTypes) Array(args *Args, reply *[2]int) error {
(*reply)[0] = args.A
(*reply)[1] = args.B
return nil
}
func listenTCP() (net.Listener, string) {
l, e := net.Listen("tcp", "127.0.0.1:0") // any available address
if e != nil {
log.Fatalf("net.Listen tcp :0: %v", e)
}
return l, l.Addr().String()
}
func startServer() {
Register(new(Arith))
Register(new(Embed))
RegisterName("net.rpc.Arith", new(Arith))
Register(BuiltinTypes{})
var l net.Listener
l, serverAddr = listenTCP()
log.Println("Test RPC server listening on", serverAddr)
go Accept(l)
HandleHTTP()
httpOnce.Do(startHttpServer)
}
func startNewServer() {
newServer = NewServer()
newServer.Register(new(Arith))
newServer.Register(new(Embed))
newServer.RegisterName("net.rpc.Arith", new(Arith))
newServer.RegisterName("newServer.Arith", new(Arith))
var l net.Listener
l, newServerAddr = listenTCP()
log.Println("NewServer test RPC server listening on", newServerAddr)
go newServer.Accept(l)
newServer.HandleHTTP(newHttpPath, "/bar")
httpOnce.Do(startHttpServer)
}
func startHttpServer() {
server := httptest.NewServer(nil)
httpServerAddr = server.Listener.Addr().String()
log.Println("Test HTTP RPC server listening on", httpServerAddr)
}
func TestRPC(t *testing.T) {
once.Do(startServer)
testRPC(t, serverAddr)
newOnce.Do(startNewServer)
testRPC(t, newServerAddr)
testNewServerRPC(t, newServerAddr)
}
func testRPC(t *testing.T, addr string) {
client, err := Dial("tcp", addr)
if err != nil {
t.Fatal("dialing", err)
}
defer client.Close()
// Synchronous calls
args := &Args{7, 8}
reply := new(Reply)
err = client.Call("Arith.Add", args, reply)
if err != nil {
t.Errorf("Add: expected no error but got string %q", err.Error())
}
if reply.C != args.A+args.B {
t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
}
// Methods exported from unexported embedded structs
args = &Args{7, 0}
reply = new(Reply)
err = client.Call("Embed.Exported", args, reply)
if err != nil {
t.Errorf("Add: expected no error but got string %q", err.Error())
}
if reply.C != args.A+args.B {
t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
}
// Nonexistent method
args = &Args{7, 0}
reply = new(Reply)
err = client.Call("Arith.BadOperation", args, reply)
// expect an error
if err == nil {
t.Error("BadOperation: expected error")
} else if !strings.HasPrefix(err.Error(), "rpc: can't find method ") {
t.Errorf("BadOperation: expected can't find method error; got %q", err)
}
// Unknown service
args = &Args{7, 8}
reply = new(Reply)
err = client.Call("Arith.Unknown", args, reply)
if err == nil {
t.Error("expected error calling unknown service")
} else if !strings.Contains(err.Error(), "method") {
t.Error("expected error about method; got", err)
}
// Out of order.
args = &Args{7, 8}
mulReply := new(Reply)
mulCall := client.Go("Arith.Mul", args, mulReply, nil)
addReply := new(Reply)
addCall := client.Go("Arith.Add", args, addReply, nil)
addCall = <-addCall.Done
if addCall.Error != nil {
t.Errorf("Add: expected no error but got string %q", addCall.Error.Error())
}
if addReply.C != args.A+args.B {
t.Errorf("Add: expected %d got %d", addReply.C, args.A+args.B)
}
mulCall = <-mulCall.Done
if mulCall.Error != nil {
t.Errorf("Mul: expected no error but got string %q", mulCall.Error.Error())
}
if mulReply.C != args.A*args.B {
t.Errorf("Mul: expected %d got %d", mulReply.C, args.A*args.B)
}
// Error test
args = &Args{7, 0}
reply = new(Reply)
err = client.Call("Arith.Div", args, reply)
// expect an error: zero divide
if err == nil {
t.Error("Div: expected error")
} else if err.Error() != "divide by zero" {
t.Error("Div: expected divide by zero error; got", err)
}
// Bad type.
reply = new(Reply)
err = client.Call("Arith.Add", reply, reply) // args, reply would be the correct thing to use
if err == nil {
t.Error("expected error calling Arith.Add with wrong arg type")
} else if !strings.Contains(err.Error(), "type") {
t.Error("expected error about type; got", err)
}
// Non-struct argument
const Val = 12345
str := fmt.Sprint(Val)
reply = new(Reply)
err = client.Call("Arith.Scan", &str, reply)
if err != nil {
t.Errorf("Scan: expected no error but got string %q", err.Error())
} else if reply.C != Val {
t.Errorf("Scan: expected %d got %d", Val, reply.C)
}
// Non-struct reply
args = &Args{27, 35}
str = ""
err = client.Call("Arith.String", args, &str)
if err != nil {
t.Errorf("String: expected no error but got string %q", err.Error())
}
expect := fmt.Sprintf("%d+%d=%d", args.A, args.B, args.A+args.B)
if str != expect {
t.Errorf("String: expected %s got %s", expect, str)
}
args = &Args{7, 8}
reply = new(Reply)
err = client.Call("Arith.Mul", args, reply)
if err != nil {
t.Errorf("Mul: expected no error but got string %q", err.Error())
}
if reply.C != args.A*args.B {
t.Errorf("Mul: expected %d got %d", reply.C, args.A*args.B)
}
// ServiceName contain "." character
args = &Args{7, 8}
reply = new(Reply)
err = client.Call("net.rpc.Arith.Add", args, reply)
if err != nil {
t.Errorf("Add: expected no error but got string %q", err.Error())
}
if reply.C != args.A+args.B {
t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
}
}
func testNewServerRPC(t *testing.T, addr string) {
client, err := Dial("tcp", addr)
if err != nil {
t.Fatal("dialing", err)
}
defer client.Close()
// Synchronous calls
args := &Args{7, 8}
reply := new(Reply)
err = client.Call("newServer.Arith.Add", args, reply)
if err != nil {
t.Errorf("Add: expected no error but got string %q", err.Error())
}
if reply.C != args.A+args.B {
t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
}
}
func TestHTTP(t *testing.T) {
once.Do(startServer)
testHTTPRPC(t, "")
newOnce.Do(startNewServer)
testHTTPRPC(t, newHttpPath)
}
func testHTTPRPC(t *testing.T, path string) {
var client *Client
var err error
if path == "" {
client, err = DialHTTP("tcp", httpServerAddr)
} else {
client, err = DialHTTPPath("tcp", httpServerAddr, path)
}
if err != nil {
t.Fatal("dialing", err)
}
defer client.Close()
// Synchronous calls
args := &Args{7, 8}
reply := new(Reply)
err = client.Call("Arith.Add", args, reply)
if err != nil {
t.Errorf("Add: expected no error but got string %q", err.Error())
}
if reply.C != args.A+args.B {
t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
}
}
func TestBuiltinTypes(t *testing.T) {
once.Do(startServer)
client, err := DialHTTP("tcp", httpServerAddr)
if err != nil {
t.Fatal("dialing", err)
}
defer client.Close()
// Map
args := &Args{7, 8}
replyMap := map[int]int{}
err = client.Call("BuiltinTypes.Map", args, &replyMap)
if err != nil {
t.Errorf("Map: expected no error but got string %q", err.Error())
}
if replyMap[args.A] != args.B {
t.Errorf("Map: expected %d got %d", args.B, replyMap[args.A])
}
// Slice
args = &Args{7, 8}
replySlice := []int{}
err = client.Call("BuiltinTypes.Slice", args, &replySlice)
if err != nil {
t.Errorf("Slice: expected no error but got string %q", err.Error())
}
if e := []int{args.A, args.B}; !reflect.DeepEqual(replySlice, e) {
t.Errorf("Slice: expected %v got %v", e, replySlice)
}
// Array
args = &Args{7, 8}
replyArray := [2]int{}
err = client.Call("BuiltinTypes.Array", args, &replyArray)
if err != nil {
t.Errorf("Array: expected no error but got string %q", err.Error())
}
if e := [2]int{args.A, args.B}; !reflect.DeepEqual(replyArray, e) {
t.Errorf("Array: expected %v got %v", e, replyArray)
}
}
// CodecEmulator provides a client-like api and a ServerCodec interface.
// Can be used to test ServeRequest.
type CodecEmulator struct {
server *Server
serviceMethod string
args *Args
reply *Reply
err error
}
func (codec *CodecEmulator) Call(serviceMethod string, args *Args, reply *Reply) error {
codec.serviceMethod = serviceMethod
codec.args = args
codec.reply = reply
codec.err = nil
var serverError error
if codec.server == nil {
serverError = ServeRequest(codec)
} else {
serverError = codec.server.ServeRequest(codec)
}
if codec.err == nil && serverError != nil {
codec.err = serverError
}
return codec.err
}
func (codec *CodecEmulator) ReadRequestHeader(req *Request) error {
req.ServiceMethod = codec.serviceMethod
req.Seq = 0
return nil
}
func (codec *CodecEmulator) ReadRequestBody(argv interface{}) error {
if codec.args == nil {
return io.ErrUnexpectedEOF
}
*(argv.(*Args)) = *codec.args
return nil
}
func (codec *CodecEmulator) WriteResponse(resp *Response, reply interface{}) error {
if resp.Error != "" {
codec.err = errors.New(resp.Error)
} else {
*codec.reply = *(reply.(*Reply))
}
return nil
}
func (codec *CodecEmulator) Close() error {
return nil
}
func TestServeRequest(t *testing.T) {
once.Do(startServer)
testServeRequest(t, nil)
newOnce.Do(startNewServer)
testServeRequest(t, newServer)
}
func testServeRequest(t *testing.T, server *Server) {
client := CodecEmulator{server: server}
defer client.Close()
args := &Args{7, 8}
reply := new(Reply)
err := client.Call("Arith.Add", args, reply)
if err != nil {
t.Errorf("Add: expected no error but got string %q", err.Error())
}
if reply.C != args.A+args.B {
t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
}
err = client.Call("Arith.Add", nil, reply)
if err == nil {
t.Errorf("expected error calling Arith.Add with nil arg")
}
}
type ReplyNotPointer int
type ArgNotPublic int
type ReplyNotPublic int
type NeedsPtrType int
type local struct{}
func (t *ReplyNotPointer) ReplyNotPointer(args *Args, reply Reply) error {
return nil
}
func (t *ArgNotPublic) ArgNotPublic(args *local, reply *Reply) error {
return nil
}
func (t *ReplyNotPublic) ReplyNotPublic(args *Args, reply *local) error {
return nil
}
func (t *NeedsPtrType) NeedsPtrType(args *Args, reply *Reply) error {
return nil
}
// Check that registration handles lots of bad methods and a type with no suitable methods.
func TestRegistrationError(t *testing.T) {
err := Register(new(ReplyNotPointer))
if err == nil {
t.Error("expected error registering ReplyNotPointer")
}
err = Register(new(ArgNotPublic))
if err == nil {
t.Error("expected error registering ArgNotPublic")
}
err = Register(new(ReplyNotPublic))
if err == nil {
t.Error("expected error registering ReplyNotPublic")
}
err = Register(NeedsPtrType(0))
if err == nil {
t.Error("expected error registering NeedsPtrType")
} else if !strings.Contains(err.Error(), "pointer") {
t.Error("expected hint when registering NeedsPtrType")
}
}
type WriteFailCodec int
func (WriteFailCodec) WriteRequest(*Request, interface{}) error {
// the panic caused by this error used to not unlock a lock.
return errors.New("fail")
}
func (WriteFailCodec) ReadResponseHeader(*Response) error {
select {}
}
func (WriteFailCodec) ReadResponseBody(interface{}) error {
select {}
}
func (WriteFailCodec) Close() error {
return nil
}
func TestSendDeadlock(t *testing.T) {
client := NewClientWithCodec(WriteFailCodec(0))
defer client.Close()
done := make(chan bool)
go func() {
testSendDeadlock(client)
testSendDeadlock(client)
done <- true
}()
select {
case <-done:
return
case <-time.After(5 * time.Second):
t.Fatal("deadlock")
}
}
func testSendDeadlock(client *Client) {
defer func() {
recover()
}()
args := &Args{7, 8}
reply := new(Reply)
client.Call("Arith.Add", args, reply)
}
func dialDirect() (*Client, error) {
return Dial("tcp", serverAddr)
}
func dialHTTP() (*Client, error) {
return DialHTTP("tcp", httpServerAddr)
}
func countMallocs(dial func() (*Client, error), t *testing.T) float64 {
once.Do(startServer)
client, err := dial()
if err != nil {
t.Fatal("error dialing", err)
}
defer client.Close()
args := &Args{7, 8}
reply := new(Reply)
return testing.AllocsPerRun(100, func() {
err := client.Call("Arith.Add", args, reply)
if err != nil {
t.Errorf("Add: expected no error but got string %q", err.Error())
}
if reply.C != args.A+args.B {
t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
}
})
}
func TestCountMallocs(t *testing.T) {
if testing.Short() {
t.Skip("skipping malloc count in short mode")
}
if runtime.GOMAXPROCS(0) > 1 {
t.Skip("skipping; GOMAXPROCS>1")
}
fmt.Printf("mallocs per rpc round trip: %v\n", countMallocs(dialDirect, t))
}
func TestCountMallocsOverHTTP(t *testing.T) {
if testing.Short() {
t.Skip("skipping malloc count in short mode")
}
if runtime.GOMAXPROCS(0) > 1 {
t.Skip("skipping; GOMAXPROCS>1")
}
fmt.Printf("mallocs per HTTP rpc round trip: %v\n", countMallocs(dialHTTP, t))
}
type writeCrasher struct {
done chan bool
}
func (writeCrasher) Close() error {
return nil
}
func (w *writeCrasher) Read(p []byte) (int, error) {
<-w.done
return 0, io.EOF
}
func (writeCrasher) Write(p []byte) (int, error) {
return 0, errors.New("fake write failure")
}
func TestClientWriteError(t *testing.T) {
w := &writeCrasher{done: make(chan bool)}
c := NewClient(w)
defer c.Close()
res := false
err := c.Call("foo", 1, &res)
if err == nil {
t.Fatal("expected error")
}
if err.Error() != "fake write failure" {
t.Error("unexpected value of error:", err)
}
w.done <- true
}
func TestTCPClose(t *testing.T) {
once.Do(startServer)
client, err := dialHTTP()
if err != nil {
t.Fatalf("dialing: %v", err)
}
defer client.Close()
args := Args{17, 8}
var reply Reply
err = client.Call("Arith.Mul", args, &reply)
if err != nil {
t.Fatal("arith error:", err)
}
t.Logf("Arith: %d*%d=%d\n", args.A, args.B, reply)
if reply.C != args.A*args.B {
t.Errorf("Add: expected %d got %d", reply.C, args.A*args.B)
}
}
func TestErrorAfterClientClose(t *testing.T) {
once.Do(startServer)
client, err := dialHTTP()
if err != nil {
t.Fatalf("dialing: %v", err)
}
err = client.Close()
if err != nil {
t.Fatal("close error:", err)
}
err = client.Call("Arith.Add", &Args{7, 9}, new(Reply))
if err != ErrShutdown {
t.Errorf("Forever: expected ErrShutdown got %v", err)
}
}
// Tests the fix to issue 11221. Without the fix, this loops forever or crashes.
func TestAcceptExitAfterListenerClose(t *testing.T) {
newServer := NewServer()
newServer.Register(new(Arith))
newServer.RegisterName("net.rpc.Arith", new(Arith))
newServer.RegisterName("newServer.Arith", new(Arith))
var l net.Listener
l, _ = listenTCP()
l.Close()
newServer.Accept(l)
}
func TestShutdown(t *testing.T) {
var l net.Listener
l, _ = listenTCP()
ch := make(chan net.Conn, 1)
go func() {
defer l.Close()
c, err := l.Accept()
if err != nil {
t.Error(err)
}
ch <- c
}()
c, err := net.Dial("tcp", l.Addr().String())
if err != nil {
t.Fatal(err)
}
c1 := <-ch
if c1 == nil {
t.Fatal(err)
}
newServer := NewServer()
newServer.Register(new(Arith))
go newServer.ServeConn(c1)
args := &Args{7, 8}
reply := new(Reply)
client := NewClient(c)
err = client.Call("Arith.Add", args, reply)
if err != nil {
t.Fatal(err)
}
// On an unloaded system 10ms is usually enough to fail 100% of the time
// with a broken server. On a loaded system, a broken server might incorrectly
// be reported as passing, but we're OK with that kind of flakiness.
// If the code is correct, this test will never fail, regardless of timeout.
args.A = 10 // 10 ms
done := make(chan *Call, 1)
call := client.Go("Arith.SleepMilli", args, reply, done)
c.(*net.TCPConn).CloseWrite()
<-done
if call.Error != nil {
t.Fatal(err)
}
}
func benchmarkEndToEnd(dial func() (*Client, error), b *testing.B) {
once.Do(startServer)
client, err := dial()
if err != nil {
b.Fatal("error dialing:", err)
}
defer client.Close()
// Synchronous calls
args := &Args{7, 8}
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
reply := new(Reply)
for pb.Next() {
err := client.Call("Arith.Add", args, reply)
if err != nil {
b.Fatalf("rpc error: Add: expected no error but got string %q", err.Error())
}
if reply.C != args.A+args.B {
b.Fatalf("rpc error: Add: expected %d got %d", reply.C, args.A+args.B)
}
}
})
}
func benchmarkEndToEndAsync(dial func() (*Client, error), b *testing.B) {
if b.N == 0 {
return
}
const MaxConcurrentCalls = 100
once.Do(startServer)
client, err := dial()
if err != nil {
b.Fatal("error dialing:", err)
}
defer client.Close()
// Asynchronous calls
args := &Args{7, 8}
procs := 4 * runtime.GOMAXPROCS(-1)
send := int32(b.N)
recv := int32(b.N)
var wg sync.WaitGroup
wg.Add(procs)
gate := make(chan bool, MaxConcurrentCalls)
res := make(chan *Call, MaxConcurrentCalls)
b.ResetTimer()
for p := 0; p < procs; p++ {
go func() {
for atomic.AddInt32(&send, -1) >= 0 {
gate <- true
reply := new(Reply)
client.Go("Arith.Add", args, reply, res)
}
}()
go func() {
for call := range res {
A := call.Args.(*Args).A
B := call.Args.(*Args).B
C := call.Reply.(*Reply).C
if A+B != C {
b.Errorf("incorrect reply: Add: expected %d got %d", A+B, C)
return
}
<-gate
if atomic.AddInt32(&recv, -1) == 0 {
close(res)
}
}
wg.Done()
}()
}
wg.Wait()
}
func BenchmarkEndToEnd(b *testing.B) {
benchmarkEndToEnd(dialDirect, b)
}
func BenchmarkEndToEndHTTP(b *testing.B) {
benchmarkEndToEnd(dialHTTP, b)
}
func BenchmarkEndToEndAsync(b *testing.B) {
benchmarkEndToEndAsync(dialDirect, b)
}
func BenchmarkEndToEndAsyncHTTP(b *testing.B) {
benchmarkEndToEndAsync(dialHTTP, b)
}

View File

@ -1,11 +1,12 @@
package server
import (
"net/rpc"
"net"
"HFish/core/rpc/core"
"HFish/utils/log"
"HFish/core/report"
"strconv"
"net"
"fmt"
)
// 上报状态结构
@ -30,7 +31,7 @@ type HFishRPCService int
// 上报状态 RPC 方法
func (t *HFishRPCService) ReportStatus(s *Status, reply *string) error {
fmt.Println(s)
// 上报 客户端 状态
go report.ReportAgentStatus(
s.AgentName,
@ -108,6 +109,9 @@ func Start(addr string) {
if err != nil {
continue
}
fmt.Println(conn.RemoteAddr())
rpc.ServeConn(conn)
}
}

Binary file not shown.

BIN
db/ipip.ipdb Normal file

Binary file not shown.

5
go.mod
View File

@ -5,13 +5,18 @@ go 1.12
require (
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394
github.com/bitly/go-simplejson v0.5.0
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/elazarl/goproxy v0.0.0-20190711103511-473e67f1d7d2
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 // indirect
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
github.com/gin-gonic/gin v1.4.0
github.com/gliderlabs/ssh v0.2.2
github.com/ipipdotnet/ipdb-go v1.2.0
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869
github.com/kr/pretty v0.1.0 // indirect
github.com/mattn/go-sqlite3 v1.11.0
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
)

11
go.sum
View File

@ -2,6 +2,10 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg=
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/elazarl/goproxy v0.0.0-20190711103511-473e67f1d7d2 h1:aZtFdDNWY/yH86JPR2WX/PN63635VsE/f/nXNPAbYxY=
@ -18,10 +22,17 @@ github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/ipipdotnet/ipdb-go v1.2.0 h1:Afa0qx/SgRevzIK8Qg1TevuD5M28kFLWbzPvU+GQJ08=
github.com/ipipdotnet/ipdb-go v1.2.0/go.mod h1:6SFLNyXDBF6q99FQvbOZJQCc2rdPrB1V5DSy4S83RSw=
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=

2
libs/ssh/arp.hf Normal file
View File

@ -0,0 +1,2 @@
Address HWtype HWaddress Flags Mask Iface
gateway ether ee:ff:ff:ff:ff:ff C eth0

26
libs/ssh/config.json Normal file
View File

@ -0,0 +1,26 @@
{
"account": "root",
"password": "root",
"hostname": "[root@web_server ~]# ",
"command": {
"default": "default",
"ls": "ls",
"ls -all": "ls_all",
"arp": "arp",
"cat /proc/cpuinfo": "cpuinfo",
"cat /etc/group": "group",
"cat /etc/hosts": "hosts",
"hostname": "hostname",
"cat /etc/inittab": "inittab",
"cat /proc/meminfo": "meminfo",
"cat /proc/passwd": "passwd",
"cat /etc/resolv.conf": "resolv",
"cat /proc/version": "version",
"ifconfig": "ifconfig",
"df -h": "df_h",
"df": "df",
"wget": "wget",
"ll": "ll",
"ll -h": "ll_h"
}
}

25
libs/ssh/cpuinfo.hf Normal file
View File

@ -0,0 +1,25 @@
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 79
model name : Intel(R) Xeon(R) CPU E5-2682 v4 @ 2.50GHz
stepping : 1
microcode : 0x1
cpu MHz : 2494.222
cache size : 40960 KB
physical id : 0
siblings : 1
core id : 0
cpu cores : 1
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap xsaveopt
bogomips : 4988.44
clflush size : 64
cache_alignment : 64
address sizes : 46 bits physical, 48 bits virtual
power management:

1
libs/ssh/default.hf Normal file
View File

@ -0,0 +1 @@
test

7
libs/ssh/df.hf Normal file
View File

@ -0,0 +1,7 @@
文件系统 1K-块 已用 可用 已用% 挂载点
/dev/vda1 41151808 14343860 24694516 37% /
devtmpfs 498608 0 498608 0% /dev
tmpfs 508084 0 508084 0% /dev/shm
tmpfs 508084 556 507528 1% /run
tmpfs 508084 0 508084 0% /sys/fs/cgroup
tmpfs 101620 0 101620 0% /run/user/0

7
libs/ssh/df_h.hf Normal file
View File

@ -0,0 +1,7 @@
文件系统 容量 已用 可用 已用% 挂载点
/dev/vda1 40G 14G 24G 37% /
devtmpfs 487M 0 487M 0% /dev
tmpfs 497M 0 497M 0% /dev/shm
tmpfs 497M 556K 496M 1% /run
tmpfs 497M 0 497M 0% /sys/fs/cgroup
tmpfs 100M 0 100M 0% /run/user/0

44
libs/ssh/group.hf Normal file
View File

@ -0,0 +1,44 @@
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:
tty:x:5:
disk:x:6:
lp:x:7:
mail:x:8:
news:x:9:
uucp:x:10:
man:x:12:
proxy:x:13:
kmem:x:15:
dialout:x:20:
fax:x:21:
voice:x:22:
cdrom:x:24:richard
floppy:x:25:richard
tape:x:26:
sudo:x:27:
audio:x:29:richard
dip:x:30:richard
www-data:x:33:
backup:x:34:
operator:x:37:
list:x:38:
irc:x:39:
src:x:40:
gnats:x:41:
shadow:x:42:
utmp:x:43:
video:x:44:richard
sasl:x:45:
plugdev:x:46:richard
staff:x:50:
games:x:60:
users:x:100:
nogroup:x:65534:
libuuid:x:101:
crontab:x:102:
vboxsf:x:103:
ssh:x:104:
richard:x:1000:

1
libs/ssh/hostname.hf Normal file
View File

@ -0,0 +1 @@
web_server

2
libs/ssh/hosts.hf Normal file
View File

@ -0,0 +1,2 @@
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6

15
libs/ssh/ifconfig.hf Normal file
View File

@ -0,0 +1,15 @@
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.1.10.100 netmask 255.255.240.0 broadcast 172.1.10.255
ether 00:16:3e:01:63:d3 txqueuelen 1000 (Ethernet)
RX packets 98016695 bytes 12961138638 (12.0 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 105965925 bytes 51727037089 (48.1 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1 (Local Loopback)
RX packets 2995757 bytes 1946922830 (1.8 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2995757 bytes 1946922830 (1.8 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

17
libs/ssh/inittab.hf Normal file
View File

@ -0,0 +1,17 @@
# inittab is no longer used when using systemd.
#
# ADDING CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.
#
# Ctrl-Alt-Delete is handled by /usr/lib/systemd/system/ctrl-alt-del.target
#
# systemd uses 'targets' instead of runlevels. By default, there are two main targets:
#
# multi-user.target: analogous to runlevel 3
# graphical.target: analogous to runlevel 5
#
# To view current default target, run:
# systemctl get-default
#
# To set a default target, run:
# systemctl set-default TARGET.target
#

21
libs/ssh/ll.hf Normal file
View File

@ -0,0 +1,21 @@
总用量 60
lrwxrwxrwx. 1 root root 7 10月 15 2017 bin -> usr/bin
dr-xr-xr-x. 5 root root 4096 4月 27 2018 boot
drwxr-xr-x 20 root root 3040 12月 5 2017 dev
drwxr-xr-x. 81 root root 4096 8月 14 23:37 etc
drwxr-xr-x. 5 root root 4096 12月 4 2017 home
lrwxrwxrwx. 1 root root 7 10月 15 2017 lib -> usr/lib
lrwxrwxrwx. 1 root root 9 10月 15 2017 lib64 -> usr/lib64
drwx------. 2 root root 16384 10月 15 2017 lost+found
drwxr-xr-x. 2 root root 4096 11月 5 2016 media
drwxr-xr-x. 2 root root 4096 11月 5 2016 mnt
drwxr-xr-x. 4 root root 4096 8月 14 23:45 opt
dr-xr-xr-x 97 root root 0 12月 5 2017 proc
dr-xr-x---. 7 root root 4096 8月 5 22:15 root
drwxr-xr-x 26 root root 840 8月 14 23:37 run
lrwxrwxrwx. 1 root root 8 10月 15 2017 sbin -> usr/sbin
drwxr-xr-x. 2 root root 4096 11月 5 2016 srv
dr-xr-xr-x 13 root root 0 12月 5 2017 sys
drwxrwxrwt. 8 root root 4096 8月 25 00:55 tmp
drwxr-xr-x. 13 root root 4096 10月 15 2017 usr
drwxr-xr-x. 19 root root 4096 10月 15 2017 var

20
libs/ssh/ll_h.hf Normal file
View File

@ -0,0 +1,20 @@
lrwxrwxrwx. 1 root root 7 10月 15 2017 bin -> usr/bin
dr-xr-xr-x. 5 root root 4.0K 4月 27 2018 boot
drwxr-xr-x 20 root root 3.0K 12月 5 2017 dev
drwxr-xr-x. 81 root root 4.0K 8月 14 23:37 etc
drwxr-xr-x. 5 root root 4.0K 12月 4 2017 home
lrwxrwxrwx. 1 root root 7 10月 15 2017 lib -> usr/lib
lrwxrwxrwx. 1 root root 9 10月 15 2017 lib64 -> usr/lib64
drwx------. 2 root root 16K 10月 15 2017 lost+found
drwxr-xr-x. 2 root root 4.0K 11月 5 2016 media
drwxr-xr-x. 2 root root 4.0K 11月 5 2016 mnt
drwxr-xr-x. 4 root root 4.0K 8月 14 23:45 opt
dr-xr-xr-x 95 root root 0 12月 5 2017 proc
dr-xr-x---. 7 root root 4.0K 8月 5 22:15 root
drwxr-xr-x 26 root root 840 8月 14 23:37 run
lrwxrwxrwx. 1 root root 8 10月 15 2017 sbin -> usr/sbin
drwxr-xr-x. 2 root root 4.0K 11月 5 2016 srv
dr-xr-xr-x 13 root root 0 12月 5 2017 sys
drwxrwxrwt. 8 root root 4.0K 8月 25 00:55 tmp
drwxr-xr-x. 13 root root 4.0K 10月 15 2017 usr
drwxr-xr-x. 19 root root 4.0K 10月 15 2017 var

1
libs/ssh/ls.hf Normal file
View File

@ -0,0 +1 @@
bin boot dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var

24
libs/ssh/ls_all.hf Normal file
View File

@ -0,0 +1,24 @@
总用量 68
dr-xr-xr-x. 18 root root 4096 12月 5 2017 .
dr-xr-xr-x. 18 root root 4096 12月 5 2017 ..
-rw-r--r-- 1 root root 0 10月 15 2017 .autorelabel
lrwxrwxrwx. 1 root root 7 10月 15 2017 bin -> usr/bin
dr-xr-xr-x. 5 root root 4096 4月 27 2018 boot
drwxr-xr-x 20 root root 3040 12月 5 2017 dev
drwxr-xr-x. 81 root root 4096 8月 14 23:37 etc
drwxr-xr-x. 5 root root 4096 12月 4 2017 home
lrwxrwxrwx. 1 root root 7 10月 15 2017 lib -> usr/lib
lrwxrwxrwx. 1 root root 9 10月 15 2017 lib64 -> usr/lib64
drwx------. 2 root root 16384 10月 15 2017 lost+found
drwxr-xr-x. 2 root root 4096 11月 5 2016 media
drwxr-xr-x. 2 root root 4096 11月 5 2016 mnt
drwxr-xr-x. 4 root root 4096 8月 14 23:45 opt
dr-xr-xr-x 95 root root 0 12月 5 2017 proc
dr-xr-x---. 7 root root 4096 8月 5 22:15 root
drwxr-xr-x 26 root root 840 8月 14 23:37 run
lrwxrwxrwx. 1 root root 8 10月 15 2017 sbin -> usr/sbin
drwxr-xr-x. 2 root root 4096 11月 5 2016 srv
dr-xr-xr-x 13 root root 0 12月 5 2017 sys
drwxrwxrwt. 8 root root 4096 8月 25 00:39 tmp
drwxr-xr-x. 13 root root 4096 10月 15 2017 usr
drwxr-xr-x. 19 root root 4096 10月 15 2017 var

44
libs/ssh/meminfo.hf Normal file
View File

@ -0,0 +1,44 @@
MemTotal: 1016168 kB
MemFree: 94900 kB
MemAvailable: 193812 kB
Buffers: 19500 kB
Cached: 190348 kB
SwapCached: 0 kB
Active: 709420 kB
Inactive: 108304 kB
Active(anon): 608272 kB
Inactive(anon): 184 kB
Active(file): 101148 kB
Inactive(file): 108120 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 92 kB
Writeback: 0 kB
AnonPages: 607908 kB
Mapped: 58488 kB
Shmem: 580 kB
Slab: 70888 kB
SReclaimable: 34864 kB
SUnreclaim: 36024 kB
KernelStack: 2560 kB
PageTables: 10072 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 508084 kB
Committed_AS: 1204784 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 8804 kB
VmallocChunk: 34359719676 kB
HardwareCorrupted: 0 kB
AnonHugePages: 24576 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 67456 kB
DirectMap2M: 980992 kB
DirectMap1G: 0 kB

21
libs/ssh/passwd.hf Normal file
View File

@ -0,0 +1,21 @@
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
sshd:x:101:65534::/var/run/sshd:/usr/sbin/nologin
richard:x:1000:1000:Richard Texas,,,:/home/richard:/bin/bash

2
libs/ssh/resolv.hf Normal file
View File

@ -0,0 +1,2 @@
nameserver 8.8.8.8
nameserver 8.8.4.4

1
libs/ssh/version.hf Normal file
View File

@ -0,0 +1 @@
Linux version 3.2.0-4-amd64 (debian-kernel@lists.debian.org) (gcc version 4.6.3 (Debian 4.6.3-14) ) #1 SMP Debian 3.2.68-1+deb7u1

4
libs/ssh/wget.hf Normal file
View File

@ -0,0 +1,4 @@
wget未指定 URL
用法: wget [选项]... [URL]...
请尝试使用“wget --help”查看更多的选项。

0
libs/telnet/config.json Normal file
View File

0
libs/telnet/ls.hfish Normal file
View File

View File

@ -0,0 +1 @@
[SSH] 127.0.0.1 - [2019-08-25 00:17:23] 已经连接 []

View File

@ -7,6 +7,7 @@ import (
)
func main() {
setting.Run()
args := os.Args
if args == nil || len(args) < 2 {
setting.Help()
@ -16,7 +17,7 @@ func main() {
} else if args[1] == "init" || args[1] == "--init" {
setting.Init()
} else if args[1] == "version" || args[1] == "--version" {
fmt.Println("v0.2")
fmt.Println("v0.3")
} else if args[1] == "run" || args[1] == "--run" {
setting.Run()
} else {

View File

@ -0,0 +1,12 @@
.spage-total{display: inline-block;margin-right: 20px;line-height: 35px;color: #049cfd;font-size:14px;}
.spage-number{display: inline-block;color: #666;font-size:14px;}
.spage-number span{position: relative;box-sizing: border-box;display: inline-block;margin-left:-1px;padding: 0px 14px;line-height: 33px;border:1px solid #ddd;text-align: center;transition: all .2s;cursor: pointer;}
.spage-number span.active{background: #049cfd;color: #fff;border-color:#049cfd;z-index: 3;}
.spage-number span.active:hover{background: #049cfd;color: #fff;border-color:#049cfd;z-index: 3;}
.spage-number span:hover{background-color: #eee;}
.spage-number span.span-disabled{cursor: not-allowed;color: #ccc;}
.spage-skip{display: inline-block;margin-left: 20px;line-height: 35px;color: #666;font-size:14px;}
.spage-skip input{box-sizing: border-box;display: inline-block;width: 45px;height: 35px;line-height: 35px;text-align: center;vertical-align: top;border:1px solid #ddd;outline: none;transition: all .2s;}
.spage-skip input:focus{border-color: #049cfd;}
.spage-skip span{background: #049cfd;display: inline-block;padding: 0px 14px;line-height: 33px;vertical-align: top;color: #fff;outline: none;border:1px solid #ddd;cursor: pointer;transition: all .2s;}
.spage-skip span:hover{background: #049cfd;color: #fff;border:1px solid #049cfd;}

View File

@ -0,0 +1,157 @@
/*
* jQuery分页插件sPage
* by 凌晨四点半
* 20190729
* v1.2.0
* https://github.com/jvbei/sPage
*/
; (function ($, window, document, undefined) {
'use strict';
var defaults = {
page: 1,//当前页
pageSize: 10,//每页显示多少条
total: 0,//数据总条数
showTotal: false,//是否显示总条数
totalTxt: "共{total}条",//数据总条数文字描述
noData: false,//没有数据时是否显示分页默认false不显示true显示第一页
showSkip: false,//是否显示跳页
showPN: true,//是否显示上下翻页
prevPage: "上一页",//上翻页按钮文字
nextPage: "下一页",//下翻页按钮文字
backFun: function (page) {
//点击分页按钮回调函数,返回当前页码
}
};
function Plugin(element, options) {
this.element = $(element);
this.settings = $.extend({}, defaults, options);
this.pageNum = 1, //记录当前页码
this.pageList = [], //页码集合
this.pageTatol = 0; //记录总页数
this.init();
}
$.extend(Plugin.prototype, {
init: function () {
this.element.empty();
this.viewHtml();
},
creatHtml: function (i) {
i == this.settings.page ? this.pageList.push('<span class="active" data-page=' + i + '>' + i + '</span>') : this.pageList.push('<span data-page=' + i + '>' + i + '</span>');
},
viewHtml: function () {
var settings = this.settings;
var pageTatol = 0;
if (settings.total > 0) {
pageTatol = Math.ceil(settings.total / settings.pageSize);
} else {
if (settings.noData) {
pageTatol = 1;
settings.page = 1;
settings.total = 0;
} else {
return;
}
}
this.pageTatol = pageTatol;
var pageArr = [];//分页元素集合减少dom重绘次数
this.pageNum = settings.page;
if (settings.showTotal) {
pageArr.push('<div class="spage-total">' + settings.totalTxt.replace(/\{(\w+)\}/gi, settings.total) + '</div>');
}
pageArr.push('<div class="spage-number">');
this.pageList = [];//页码元素集合,包括上下页
if (settings.showPN) {
settings.page == 1 ? this.pageList.push('<span class="span-disabled" data-page="prev">' + settings.prevPage + '</span>') : this.pageList.push('<span data-page="prev">' + settings.prevPage + '</span>');
}
if (pageTatol <= 6) {
for (var i = 1; i < pageTatol + 1; i++) {
this.creatHtml(i);
}
} else {
if (settings.page < 5) {
for (var i = 1; i <= 5; i++) {
this.creatHtml(i);
}
this.pageList.push('<span data-page="none">...</span><span data-page=' + pageTatol + '>' + pageTatol + '</span>');
} else if (settings.page > pageTatol - 4) {
this.pageList.push('<span data-page="1">1</span><span data-page="none">...</span>');
for (var i = pageTatol - 4; i <= pageTatol; i++) {
this.creatHtml(i);
}
} else {
this.pageList.push('<span data-page="1">1</span><span data-page="none">...</span>');
for (var i = settings.page - 2; i <= Number(settings.page) + 2; i++) {
this.creatHtml(i);
}
this.pageList.push('<span data-page="none">...</span><span data-page=' + pageTatol + '>' + pageTatol + '</span>');
}
}
if (settings.showPN) {
settings.page == pageTatol ? this.pageList.push('<span class="span-disabled" data-page="next">' + settings.nextPage + '</span>') : this.pageList.push('<span data-page="next">' + settings.nextPage + '</span>');
}
pageArr.push(this.pageList.join(''));
pageArr.push('</div>');
if (settings.showSkip) {
pageArr.push('<div class="spage-skip">跳至&nbsp;<input type="text" value="' + settings.page + '"/>&nbsp;&nbsp;&nbsp;<span data-page="go">确定</span></div>');
}
this.element.html(pageArr.join(''));
this.clickBtn();
},
clickBtn: function () {
var that = this;
var settings = this.settings;
var ele = this.element;
var pageTatol = this.pageTatol;
this.element.off("click", "span");
this.element.on("click", "span", function () {
var pageText = $(this).data("page");
switch (pageText) {
case "prev":
settings.page = settings.page - 1 >= 1 ? settings.page - 1 : 1;
pageText = settings.page;
break;
case "next":
settings.page = Number(settings.page) + 1 <= pageTatol ? Number(settings.page) + 1 : pageTatol;
pageText = settings.page;
break;
case "none":
return;
case "go":
var p = parseInt(ele.find("input").val());
if (/^[0-9]*$/.test(p) && p >= 1 && p <= pageTatol) {
settings.page = p;
pageText = p;
} else {
return;
}
break;
default:
settings.page = pageText;
}
// 点击或跳转当前页码不执行任何操作
if (pageText == that.pageNum) {
return;
}
that.pageNum = settings.page;
that.viewHtml();
settings.backFun(pageText);
});
this.element.on("keyup", "input", function (event) {
if (event.keyCode == 13) {
var p = parseInt(ele.find("input").val());
if (/^[0-9]*$/.test(p) && p >= 1 && p <= pageTatol && p != that.pageNum) {
settings.page = p;
that.pageNum = p;
that.viewHtml();
settings.backFun(p);
} else {
return;
}
}
});
}
});
$.fn.sPage = function (options) {
return new Plugin(this, options);
}
})(jQuery, window, document);

23
utils/cors/cors.go Normal file
View File

@ -0,0 +1,23 @@
package cors
import (
"github.com/gin-gonic/gin"
"net/http"
)
// 解决跨域问题
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
c.Header("Access-Control-Allow-Credentials", "true")
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
c.Next()
}
}

View File

@ -4,6 +4,8 @@ import (
"HFish/error"
"fmt"
"os"
"io/ioutil"
"HFish/utils/log"
)
func Output(result string, path string) {
@ -22,3 +24,13 @@ func Output(result string, path string) {
fmt.Println(result)
}
}
func ReadLibsText(typex string, name string) string {
text, err := ioutil.ReadFile("./libs/" + typex + "/" + name + ".hf")
if err != nil {
log.Pr("HFish", "127.0.0.1", "读取文件失败", err)
}
return string(text[:])
}

View File

@ -11,10 +11,12 @@ import (
"fmt"
"HFish/utils/try"
"HFish/utils/log"
"github.com/ipipdotnet/ipdb-go"
)
// 爬虫 ip138 获取 ip 地理信息
func Get(ip string) string {
// ~~~~~~ 暂时废弃,采用 IPIP
func GetIp138(ip string) string {
result := ""
try.Try(func() {
resp, err := http.Get("http://ip138.com/ips138.asp?ip=" + ip)
@ -67,3 +69,10 @@ func GetLocalIp() string {
return ""
}
// 采用 IPIP 本地库
func GetIp(ip string) (string, string, string) {
db, _ := ipdb.NewCity("./db/ipip.ipdb")
ipInfo, _ := db.FindMap(ip, "CN")
return ipInfo["country_name"], ipInfo["region_name"], ipInfo["city_name"]
}

18
utils/json/json.go Normal file
View File

@ -0,0 +1,18 @@
package json
import (
"github.com/bitly/go-simplejson"
"HFish/utils/log"
"io/ioutil"
)
func Get(typex string) (*simplejson.Json, error) {
json, err := ioutil.ReadFile("./libs/" + typex + "/config.json")
if err != nil {
log.Pr("HFish", "127.0.0.1", "读取文件失败", err)
}
res, err := simplejson.NewJson(json)
return res, err
}

View File

@ -18,6 +18,8 @@ import (
"HFish/core/protocol/telnet"
"HFish/core/rpc/server"
"HFish/core/rpc/client"
"HFish/view/api"
"HFish/utils/cors"
)
func RunWeb(template string, index string, static string, url string) http.Handler {
@ -34,6 +36,17 @@ func RunWeb(template string, index string, static string, url string) http.Handl
c.HTML(http.StatusOK, index, gin.H{})
})
// API 启用状态
apiStatus := conf.Get("api", "status")
// 判断 API 是否启用
if apiStatus == "1" {
// 启动 WEB蜜罐 API
r.Use(cors.Cors())
webUrl := conf.Get("api", "web_url")
r.POST(webUrl, api.ReportWeb)
}
return r
}
@ -51,6 +64,17 @@ func RunDeep(template string, index string, static string, url string) http.Hand
c.HTML(http.StatusOK, index, gin.H{})
})
// API 启用状态
apiStatus := conf.Get("api", "status")
// 判断 API 是否启用
if apiStatus == "1" {
// 启动 暗网蜜罐 API
r.Use(cors.Cors())
deepUrl := conf.Get("api", "deep_url")
r.POST(deepUrl, api.ReportDeepWeb)
}
return r
}
@ -108,7 +132,7 @@ func Run() {
ftpStatus := conf.Get("ftp", "status")
// 判断 FTP 蜜罐 是否开启
if ftpStatus == "1" {
if ftpStatus != "0" {
ftpAddr := conf.Get("ftp", "addr")
go ftp.Start(ftpAddr)
}
@ -119,7 +143,7 @@ func Run() {
telnetStatus := conf.Get("telnet", "status")
// 判断 Telnet 蜜罐 是否开启
if telnetStatus == "1" {
if telnetStatus != "0" {
telnetAddr := conf.Get("telnet", "addr")
go telnet.Start(telnetAddr)
}
@ -141,7 +165,7 @@ func Run() {
mysqlStatus := conf.Get("mysql", "status")
// 判断 Mysql 蜜罐 是否开启
if mysqlStatus == "1" {
if mysqlStatus != "0" {
mysqlAddr := conf.Get("mysql", "addr")
// 利用 Mysql 服务端 任意文件读取漏洞
@ -156,7 +180,7 @@ func Run() {
redisStatus := conf.Get("redis", "status")
// 判断 Redis 蜜罐 是否开启
if redisStatus == "1" {
if redisStatus != "0" {
redisAddr := conf.Get("redis", "addr")
go redis.Start(redisAddr)
}
@ -167,7 +191,7 @@ func Run() {
sshStatus := conf.Get("ssh", "status")
// 判断 SSG 蜜罐 是否开启
if sshStatus == "1" {
if sshStatus != "0" {
sshAddr := conf.Get("ssh", "addr")
go ssh.Start(sshAddr)
}
@ -178,7 +202,7 @@ func Run() {
webStatus := conf.Get("web", "status")
// 判断 Web 蜜罐 是否开启
if webStatus == "1" {
if webStatus != "0" {
webAddr := conf.Get("web", "addr")
webTemplate := conf.Get("web", "template")
webStatic := conf.Get("web", "static")
@ -201,7 +225,7 @@ func Run() {
deepStatus := conf.Get("deep", "status")
// 判断 暗网 Web 蜜罐 是否开启
if deepStatus == "1" {
if deepStatus != "0" {
deepAddr := conf.Get("deep", "addr")
deepTemplate := conf.Get("deep", "template")
deepStatic := conf.Get("deep", "static")
@ -273,7 +297,7 @@ func Help() {
{K || __ _______ __
| PP / // / __(_)__ / /
| || / _ / _// (_-</ _ \
(__\\ /_//_/_/ /_/___/_//_/ v0.2
(__\\ /_//_/_/ /_/___/_//_/ v0.3
`
fmt.Println(color.Yellow(logo))
fmt.Println(color.White(" A Safe and Active Attack Honeypot Fishing Framework System for Enterprises."))

View File

@ -11,6 +11,7 @@ import (
"HFish/utils/is"
)
// 上报WEB蜜罐
func ReportWeb(c *gin.Context) {
name := c.PostForm("name")
info := c.PostForm("info")
@ -34,6 +35,7 @@ func ReportWeb(c *gin.Context) {
}
}
// 上报暗网蜜罐
func ReportDeepWeb(c *gin.Context) {
name := c.PostForm("name")
info := c.PostForm("info")
@ -63,3 +65,10 @@ func GetIpList(c *gin.Context) {
result := dbUtil.Query(sql)
c.JSON(http.StatusOK, error.ErrSuccess(result))
}
// 获取记录黑客IP
func GetFishInfo(c *gin.Context) {
sql := `select * from hfish_info ORDER BY id desc`
result := dbUtil.Query(sql)
c.JSON(http.StatusOK, error.ErrSuccess(result))
}

View File

@ -36,3 +36,13 @@ func GetColony(c *gin.Context) {
result := dbUtil.Query(sql)
c.JSON(http.StatusOK, error.ErrSuccess(result))
}
// 删除集群
func PostColonyDel(c *gin.Context) {
id := c.PostForm("id")
sqlDel := `delete from hfish_colony where id=?;`
dbUtil.Delete(sqlDel, id)
c.JSON(http.StatusOK, error.ErrSuccessNull())
}

View File

@ -7,6 +7,7 @@ import (
"HFish/error"
"HFish/utils/page"
"strconv"
"strings"
)
// 蜜罐 页面
@ -19,6 +20,7 @@ func GetFishList(c *gin.Context) {
p, _ := c.GetQuery("page")
pageSize, _ := c.GetQuery("pageSize")
typex, _ := c.GetQuery("type")
colony, _ := c.GetQuery("colony")
soText, _ := c.GetQuery("so_text")
pInt, _ := strconv.ParseInt(p, 10, 64)
@ -26,7 +28,7 @@ func GetFishList(c *gin.Context) {
pageStart := page.Start(pInt, pageSizeInt)
sql := `select id,type,project_name,agent,ip,ip_info,create_time from hfish_info where 1=1`
sql := `select id,type,project_name,agent,ip,country,region,city,create_time,info from hfish_info where 1=1`
sqlx := `select count(1) as sum from hfish_info where 1=1`
sqlStatus := 0
@ -36,9 +38,9 @@ func GetFishList(c *gin.Context) {
sqlStatus = 1
}
if soText != "" {
sql = sql + ` and (project_name like ? or ip like ?)`
sqlx = sqlx + ` and type=?`
if colony != "all" {
sql = sql + ` and agent=?`
sqlx = sqlx + ` and agent=?`
if sqlStatus == 1 {
sqlStatus = 3
} else {
@ -46,51 +48,118 @@ func GetFishList(c *gin.Context) {
}
}
if soText != "" {
sql = sql + ` and (project_name like ? or ip like ?)`
sqlx = sqlx + ` and type=?`
if sqlStatus == 1 {
sqlStatus = 4
} else if sqlStatus == 2 {
sqlStatus = 5
} else if sqlStatus == 3 {
sqlStatus = 6
} else {
sqlStatus = 7
}
}
sql = sql + ` ORDER BY id desc LIMIT ?,?;`
if sqlStatus == 0 {
result := dbUtil.Query(sql, pageStart, pageSizeInt)
resultx := dbUtil.Query(sqlx)
pageCount := resultx[0]["sum"].(int64)
pageCount = page.TotalPage(pageCount, pageSizeInt)
totalCount := resultx[0]["sum"].(int64)
pageCount := page.TotalPage(totalCount, pageSizeInt)
c.JSON(http.StatusOK, gin.H{
"data": result,
"pageCount": pageCount,
"page": p,
"data": result,
"pageCount": pageCount,
"totalCount": totalCount,
"page": p,
})
} else if sqlStatus == 1 {
result := dbUtil.Query(sql, typex, pageStart, pageSizeInt)
resultx := dbUtil.Query(sqlx, typex)
pageCount := resultx[0]["sum"].(int64)
pageCount = page.TotalPage(pageCount, pageSizeInt)
totalCount := resultx[0]["sum"].(int64)
pageCount := page.TotalPage(totalCount, pageSizeInt)
c.JSON(http.StatusOK, gin.H{
"data": result,
"pageCount": pageCount,
"page": p,
"data": result,
"pageCount": pageCount,
"totalCount": totalCount,
"page": p,
})
} else if sqlStatus == 2 {
result := dbUtil.Query(sql, "%"+soText+"%", "%"+soText+"%", pageStart, pageSizeInt)
resultx := dbUtil.Query(sqlx, "%"+soText+"%", "%"+soText+"%")
pageCount := resultx[0]["sum"].(int64)
pageCount = page.TotalPage(pageCount, pageSizeInt)
result := dbUtil.Query(sql, colony, pageStart, pageSizeInt)
resultx := dbUtil.Query(sqlx, colony)
totalCount := resultx[0]["sum"].(int64)
pageCount := page.TotalPage(totalCount, pageSizeInt)
c.JSON(http.StatusOK, gin.H{
"data": result,
"pageCount": pageCount,
"page": p,
"data": result,
"pageCount": pageCount,
"totalCount": totalCount,
"page": p,
})
} else if sqlStatus == 3 {
result := dbUtil.Query(sql, typex, "%"+soText+"%", "%"+soText+"%", pageStart, pageSizeInt)
resultx := dbUtil.Query(sqlx, typex, "%"+soText+"%", "%"+soText+"%")
pageCount := resultx[0]["sum"].(int64)
pageCount = page.TotalPage(pageCount, pageSizeInt)
result := dbUtil.Query(sql, typex, colony, pageStart, pageSizeInt)
resultx := dbUtil.Query(sqlx, typex, colony)
totalCount := resultx[0]["sum"].(int64)
pageCount := page.TotalPage(totalCount, pageSizeInt)
c.JSON(http.StatusOK, gin.H{
"data": result,
"pageCount": pageCount,
"page": p,
"data": result,
"pageCount": pageCount,
"totalCount": totalCount,
"page": p,
})
} else if sqlStatus == 4 {
result := dbUtil.Query(sql, typex, "%"+soText+"%", "%"+soText+"%", pageStart, pageSizeInt)
resultx := dbUtil.Query(sqlx, typex, "%"+soText+"%", "%"+soText+"%")
totalCount := resultx[0]["sum"].(int64)
pageCount := page.TotalPage(totalCount, pageSizeInt)
c.JSON(http.StatusOK, gin.H{
"data": result,
"pageCount": pageCount,
"totalCount": totalCount,
"page": p,
})
} else if sqlStatus == 5 {
result := dbUtil.Query(sql, colony, "%"+soText+"%", "%"+soText+"%", pageStart, pageSizeInt)
resultx := dbUtil.Query(sqlx, colony, "%"+soText+"%", "%"+soText+"%")
totalCount := resultx[0]["sum"].(int64)
pageCount := page.TotalPage(totalCount, pageSizeInt)
c.JSON(http.StatusOK, gin.H{
"data": result,
"pageCount": pageCount,
"totalCount": totalCount,
"page": p,
})
} else if sqlStatus == 6 {
result := dbUtil.Query(sql, typex, colony, "%"+soText+"%", "%"+soText+"%", pageStart, pageSizeInt)
resultx := dbUtil.Query(sqlx, typex, colony, "%"+soText+"%", "%"+soText+"%")
totalCount := resultx[0]["sum"].(int64)
pageCount := page.TotalPage(totalCount, pageSizeInt)
c.JSON(http.StatusOK, gin.H{
"data": result,
"pageCount": pageCount,
"totalCount": totalCount,
"page": p,
})
} else if sqlStatus == 7 {
result := dbUtil.Query(sql, "%"+soText+"%", "%"+soText+"%", pageStart, pageSizeInt)
resultx := dbUtil.Query(sqlx, "%"+soText+"%", "%"+soText+"%")
totalCount := resultx[0]["sum"].(int64)
pageCount := page.TotalPage(totalCount, pageSizeInt)
c.JSON(http.StatusOK, gin.H{
"data": result,
"pageCount": pageCount,
"totalCount": totalCount,
"page": p,
})
}
}
@ -98,8 +167,15 @@ func GetFishList(c *gin.Context) {
// 删除蜜罐
func PostFishDel(c *gin.Context) {
id := c.PostForm("id")
sqlDel := `delete from hfish_info where id=?;`
dbUtil.Delete(sqlDel, id)
idx := strings.Split(id, ",")
// 暂时此种方式,待优化
for _, x := range idx {
sqlDel := `delete from hfish_info where id in (?);`
dbUtil.Delete(sqlDel, x)
}
c.JSON(http.StatusOK, error.ErrSuccessNull())
}
@ -111,9 +187,16 @@ func GetFishInfo(c *gin.Context) {
c.JSON(http.StatusOK, error.ErrSuccess(result))
}
// 获取蜜罐分类信息
// 获取蜜罐分类信息,集群信息
func GetFishTypeInfo(c *gin.Context) {
sql := `select type from hfish_info GROUP BY type;`
result := dbUtil.Query(sql)
c.JSON(http.StatusOK, error.ErrSuccess(result))
sqlInfoType := `select type from hfish_info GROUP BY type;`
resultInfoType := dbUtil.Query(sqlInfoType)
sqlColonyName := `select agent_name from hfish_colony GROUP BY agent_name;`
resultColonyName := dbUtil.Query(sqlColonyName)
c.JSON(http.StatusOK, gin.H{
"resultInfoType": resultInfoType,
"resultColonyName": resultColonyName,
})
}

View File

@ -9,96 +9,47 @@ import (
"HFish/view/setting"
"github.com/gin-gonic/gin"
"HFish/view/login"
"HFish/utils/conf"
"net/http"
"HFish/utils/is"
"HFish/utils/cors"
)
// 解决跨域问题
func cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
c.Header("Access-Control-Allow-Credentials", "true")
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
c.Next()
}
}
func LoadUrl(r *gin.Engine) {
// 判断是否为 RPC 客户端
if is.Rpc() {
/* RPC 客户端 */
/* RPC 服务端 */
// 登录
r.GET("/login", login.Html)
r.POST("/login", login.Login)
r.GET("/logout", login.Logout)
// API 接口
// WEB 上报蜜罐信息
apiStatus := conf.Get("api", "status")
// 仪表盘
r.GET("/", login.Jump, dashboard.Html)
r.GET("/dashboard", login.Jump, dashboard.Html)
r.GET("/get/dashboard/data", login.Jump, dashboard.GetFishData)
// 判断 API 是否启用
if apiStatus == "1" {
r.Use(cors())
// 蜜罐列表
r.GET("/fish", login.Jump, fish.Html)
r.GET("/get/fish/list", login.Jump, fish.GetFishList)
r.GET("/get/fish/info", login.Jump, fish.GetFishInfo)
r.GET("/get/fish/typeList", login.Jump, fish.GetFishTypeInfo)
r.POST("/post/fish/del", login.Jump, fish.PostFishDel)
webUrl := conf.Get("api", "web_url")
deepUrl := conf.Get("api", "deep_url")
// 分布式集群
r.GET("/colony", login.Jump, colony.Html)
r.GET("/get/colony/list", login.Jump, colony.GetColony)
r.POST("/post/colony/del", login.Jump, colony.PostColonyDel)
r.POST(webUrl, api.ReportWeb)
r.POST(deepUrl, api.ReportDeepWeb)
}
} else {
/* RPC 服务端 */
// 登录
r.GET("/login", login.Html)
r.POST("/login", login.Login)
r.GET("/logout", login.Logout)
// 邮件群发
r.GET("/mail", login.Jump, mail.Html)
r.POST("/post/mail/sendEmail", login.Jump, mail.SendEmailToUsers)
// 仪表盘
r.GET("/", login.Jump, dashboard.Html)
r.GET("/dashboard", login.Jump, dashboard.Html)
r.GET("/get/dashboard/data", login.Jump, dashboard.GetFishData)
// 设置
r.GET("/setting", login.Jump, setting.Html)
r.GET("/get/setting/info", login.Jump, setting.GetSettingInfo)
r.POST("/post/setting/update", login.Jump, setting.UpdateEmailInfo)
r.POST("/post/setting/updateAlertMail", login.Jump, setting.UpdateAlertMail)
r.POST("/post/setting/checkSetting", login.Jump, setting.UpdateStatusSetting)
// 蜜罐列表
r.GET("/fish", login.Jump, fish.Html)
r.GET("/get/fish/list", login.Jump, fish.GetFishList)
r.GET("/get/fish/info", login.Jump, fish.GetFishInfo)
r.GET("/get/fish/typeList", login.Jump, fish.GetFishTypeInfo)
r.POST("/post/fish/del", login.Jump, fish.PostFishDel)
// 分布式集群
r.GET("/colony", login.Jump, colony.Html)
r.GET("/get/colony/list", login.Jump, colony.GetColony)
// 邮件群发
r.GET("/mail", login.Jump, mail.Html)
r.POST("/post/mail/sendEmail", login.Jump, mail.SendEmailToUsers)
// 设置
r.GET("/setting", login.Jump, setting.Html)
r.GET("/get/setting/info", login.Jump, setting.GetSettingInfo)
r.POST("/post/setting/update", login.Jump, setting.UpdateEmailInfo)
r.POST("/post/setting/updateAlertMail", login.Jump, setting.UpdateAlertMail)
r.POST("/post/setting/checkSetting", login.Jump, setting.UpdateStatusSetting)
// API 接口
// WEB 上报蜜罐信息
apiStatus := conf.Get("api", "status")
// 判断 API 是否启用
if apiStatus == "1" {
r.Use(cors())
webUrl := conf.Get("api", "web_url")
deepUrl := conf.Get("api", "deep_url")
r.POST(webUrl, api.ReportWeb)
r.POST(deepUrl, api.ReportDeepWeb)
r.GET("/api/v1/get/ip", api.GetIpList)
}
}
// API 接口
// 解决跨域问题
r.Use(cors.Cors())
r.GET("/api/v1/get/ip", api.GetIpList)
r.GET("/api/v1/get/fish_info", api.GetFishInfo)
}