Luckpiky 2019-10-20T14:32:19+00:00 luckpiky@163.com 动态规划 2019-10-18T13:36:30+00:00 luckpiky http://luckpiky.github.io/dp 走方格问题
#include <stdio.h>

int v1[3][5] = { {1,5,7,6,8},{4,7,4,4,9},{10,3,2,3,2} };

int v2[3][5] = {0};

int min(int a, int b) 
{
    return a < b ? a : b;
}


/*
 * 给定m行n列的网格,每个格子(i, j)里都一个非负数v[i][j]
 * 求一个从左上角(0, 0)到右下角的路径,每一步只能向下或者向右走一步
 * 使得路径上的格子里的数字之和最小
 * 输出最小数字和
 * 
 * 通过动态规划进行计算
 * f(n,m) = min(f(n, m - 1), f(n - 1, m)) + v[n][m]
 * f(n,m)的前一步,是向上或者向左走,取最小值
 * 特殊情况:
 * 1. f(0,0) = v[0][0]
 * 2. n == 0 或 m == 0, 则只能选择一边
 */
int get_min_by_dp(int n, int m)
{
    if (n == 0 && m == 0) {
        v2[0][0] = v1[0][0];
        return v2[0][0];
    }

    if (n == 0) {
        v2[n][m] = get_min_by_dp(n, m - 1) + v1[n][m];
        return v2[n][m];
    }

    if (m == 0) {
        v2[n][m] = get_min_by_dp(n - 1, m) + v1[n][m];
        return v2[n][m];
    }

    int d1 = get_min_by_dp(n - 1, m);
    int d2 = get_min_by_dp(n, m - 1);

    v2[n][m] = min(d1, d2) + v1[n][m];
    return v2[n][m];
}

int main()
{
    int min = get_min_by_dp(2, 4);

    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 5 ; j++) {
            printf("%d(%d)\t", v1[i][j], v2[i][j]);
        }

        printf("\n");
    }

    printf("%d\n", min);

    return 0;
}

数字解密问题

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <memory.h>

/*
 * 有一段A-Z组成字母信息串被加密数字串,加密方式A-1,B-2...给定加密后的数字串有多少种解密方式?
 * 加密方式为:A->1, B->2, …, Z->26
 * 
 * 设数字串长度为N,要求数字串前N个字符的解密方式数,需要知道数字串前N-1和N-2个字符的解密方式数。
 * 状态:设数字串S前 i 个数字解密成字母串有 f[i] 种方式。
 * 
 * 转移方程:
 * f(i) = f(i-1) | s[i-1] + f(i-2) | s[i-2]s[i-1]
 * 
 * 初始值:
 * f(0) = 1,空串是一种解密
 * 
 * 边界:
 * f(1) = 1,只有一个数字,也能转换为一个字母
 */
int get_case(char *s)
{
    int len = strlen(s);
    if (len == 0) {
        return 1;
    }

    int *f = (int *)malloc(len+1);
    if (f == NULL) {
        return 0;
    }

    f[0] = 1;
    f[1] = 1;

    for (int i = 2; i < len + 1; i++) {
        f[i] = 0;

        // 一个字母时:
        if (s[i-1] >= '0' && s[i-1] <= '9') {
            f[i] += f[i-1];
        }

        //两个字母时:
        if ((s[i-2] == '1' || s[i-2] == '2') && 
            (s[i-1] >= '0' && s[i-1] <= '6')) {
            f[i] += f[i-2];
        }

        printf("f(%d):%d\n", i, f[i]);
    }

    return f[len];
}


int main()
{
    char *s = "122227";

    int num = get_case(s);
    printf("%d\n", num);
}

区间型动态规划

最少平方数

#include <vector>
#include <iostream>
using namespace std;

/**
 * 给定一个正整数n,问最少可以将n分成几个完全平方数(1,4,9,...)之和?
 * 
 * 关注最优策略中最后一个完全平方数 j2,最优策略中n-j2 也一定被分成最少的完全平方数之和。
 * 所以需要知道n-j2 最少被分成几个完全平方数之和。原来是求 n 最少被分成几个完全平方数之和。
 * 状态:设 f[i] 表示 i 最少被分成几个完全平方数之和
 * 
 * 转移方程:
 * f[i] = min(f[i+j*j] + 1), 1 <= j*j <= i
*/
int get_min_num(int n)
{
    vector<int> f(n+1);

    f[0] = 0;
    f[1] = 1;

    for (int i = 2; i < n + 1; i++) {
        int min = 0x7FFFFFFF;
        for (int j = 1; j < i / 2 + 1; j++) {
            if (i < j * j) {
                break;
            }

            int temp = f[i - j * j];
            min = min > temp ? temp : min;
        }

        f[i] = min + 1;
        printf("f(%d):%d\n", i, f[i]);
    }

    return f[n];
}


int main()
{
    int min = get_min_num(10);
    printf("min=%d\n", min);
}

最少回文切割问题

#include <iostream>
#include <vector>
#include <string.h>
using namespace std;

/**
 * 给定一个字符串s[0...N-1],要求将这个字符串划分成若干段,每段都是一个都是一个回文串(正反看起来都一样)龟最少划分多少次?
 * 如:"aab"   划分最少1次,"aa","b"
 * 
 * 确定状态:
 * 最后一步:关注最优策略中最后一段的回文,为s[j...N-1]
 * 子问题:求s前N个字符s[0...N-1]最少划分几个回文,需要知道s前j个字符,最少划分成几个回文。
 * 状态:设s前i个字符s[0...i-1],最少可以划分成f[i]个回文
 * 转移方程:f[i] = min(f[j]+1,j=0...i-1),且s[j...i-1]是回文串
*/
int get_min_cut(char *s)
{
    int n = strlen(s);

    vector<vector<int>> p(n, vector<int> (n));
    for (int i = n - 1 ; i >= 0; i--) {
        // 记录i开始,到j结束的字符串是否是回文
        for (int j = i; j <= n - 1; j++) {
            if (s[i] == s[j] && (j - i < 2 || p[i+1][j-1] == 1)) {
                p[i][j] = 1;
                printf("%d,%d\n", i, j);
            }
        }
    }

    vector<int> f(n+1);

    f[0] = 0;
    f[1] = 1;

    for(int i = 2; i <= n; i++) {

        int min = 0x7FFFFFFF;
        for (int j = 0; j < i; j++) {
            
            if (p[j][i-1] == 1) {
                min = f[j] + 1 < min ? f[j] + 1 : min;
            }
        }

        f[i] = min;
        printf("f(%d):%d\n", i,f[i]);
    }
    
    return f[n] - 1;
}


int main()
{
    int num = get_min_cut("aaa");
    printf("num=%d\n", num);
}
]]>
markdown 2019-07-29T00:40:30+00:00 luckpiky http://luckpiky.github.io/markdown Markdown

1 字体

加粗,两个*: **粗体字 **

斜体,一个: *斜体字

斜体加粗,三个* : 粗斜体

删除问题,两个~: 删除线

2 引用

引用文字,加> :

引用嵌套,多个> :

这是引用

嵌套引用

再次嵌套

3 分割线

三个或者三个以上的 - 或者 *


4 图片

![图片alt](图片地址 ''图片title'')

图片alt

5 超链接

[超链接名](超链接地址 "超链接title")

超链接名

新窗口的超链接:
<a href="超链接地址" target="_blank">超链接名</a>

新窗口超链接名

6 列表

无序列表用 - + * 任何一种都可以
嵌套:上一级和下一级之间敲三个空格即可
  • aaaaaaa
  • bbbbbb
    • cccc
    • ddd
有序列表:数字加点
  1. aaaa
  2. bbbb
  3. cccc

7 表格

表头|表头|表头
---|:--:|---:
内容|内容|内容
内容|内容|内容

第二行分割表头和内容。
- 有一个就行,为了对齐,多加了几个
文字默认居左
-两边加:表示文字居中
-右边加:表示文字居右
注:原生的语法两边都要用 | 包起来。此处省略
表头 表头 表头
内容 内容 内容
内容 内容 内容

8 代码

单行代码:代码之间分别用一个反引号包起来
多行,这里已经在用了:三个`

printf("Hello, markdown.")

def go()
    print("go")
    go = go + 1
]]>
UML学习 2018-12-29T20:10:30+00:00 luckpiky http://luckpiky.github.io/uml1 UML学习笔记

1 UML核心元素

1.1 版型(stereotype)

也称“类型”,“构造型”。是对UML元素基础定义的扩展,在同一个元素基础定义的基础上赋予特别的含义,使得这个元素适用于特定的场合。是一种扩展手段。

1.2 参与者(actor)

actor是在系统之外与系统交互的某人或某事物,边界之内的所有人和事物都不是参与者。
谁是actor?
用例一定存在actor,可能是一个人,可能是定时器,也可能是一个消息。actor一定是直接并且主动的向系统发出动作并获得反馈的,否则就不是actor。如下几个例子:

机票购买者通过登陆网站购买机票,机票购买者就是actor
机票购买者通过呼叫中心,由人工座席操作定票系统购买机票,人工座席就是actor
机票购买这通过呼叫中心的自动语音预定机票,呼叫中心就是actor
如果让呼叫中心成为订票系统的一个子系统,并且机票购买者可以自主通过人工座席,自动语音,登陆网站预定机票,则机票购买者就是actor,人工座席是业务工人。

业务主角(business actor)

业务主角是与业务系统有着交互的人和事务,他们用来确定业务范围。业务主角是参与者的一个版型。业务主角是针对业务人员而非计算机。

业务工人(business worker)

记住参与者是在业务系统外的,所以业务系统中的人,就不是参与者。他是主动向系统发出动作的吗?他有完整的业务目标吗?系统是为他服务的吗?

1.3 用例(usecase)

用例的特征

  • 用例是相对独立的
  • 用例的执行结果对参与者来说是可观测的和有意义的
  • 事物应该由参与者发起,不存在没有参与者的用例,用例不应该自动启动,也不应该启动另外一个用例
  • 用例必然使用动宾短语的形式出现
  • 一个用例就是一个需求单元,分析单元,设计单元,开发单元,测试单元,甚至部署单元

用例的粒度

用例的粒度以每个用例能够描述一个完整的事件流为宜,即一个用例描述一项完整业务中的一个步骤。同一个需求阶段,所有的用例粒度都是一个量级的。

使用plantUML写用例

actor定义角色,或者:xxx: as yyy 定义角色以及别名
usecase定义用例 可以用–>进行关联,:增加描述

@startuml

usecase 购买机票 as "购买机票\n这里可以添加注释,还可以分行
--
分行
===
再分行"


:机票购买人: as user1
actor 人工座席

user1 -> (购买机票) :网站,自动语音
人工座席 ->  (购买机票) : 内部系统

@enduml

结果:
image

1.4 业务实体

业务实体代表业务角色执行业务用例时所处理或使用的事物。业务实体是参与者在完成其业务目标的过程中使用到的或创建出来的,业务实体必须至少被一个业务场景用例使用或者创建,业务实体是类的一个版型,具有对象的所有性质,包括属性和方法,且只应当包含它本身固有的特性。

1.5 包

主要作用是容纳并为其他元素分类,可以容纳任何UML元素。

分包的原则

  • 高内聚:分入同一个包中的元素互相联系紧密,具有某些相同的性质,使得包可以抽象出一些接口来代表包内事物与包外事物的交互。
  • 松耦合:修改包中的任何一个元素,包中的其他元素不受影响。
  • 依赖关系不传递:如果难以做到完全解除依赖关系,至少应当保证包之间的依赖关系不被传递。
  • 避免双向依赖或者循环依赖,依赖关系应该是单向的。

    几种类型的包

  • 领域包:用于分类业务领域内的业务单元,每个包代表业务的一个领域。
  • 之系统:用于分类系统内的逻辑对象,并形成之系统。
  • 组织结构:用于分类业务领域中的组织结构,可以直接表述企业的组织结构。
  • 层:分类软件中的层次,可以描述软件的架构信息。

    1.6 分析类

    边界类

    边界类的场景:

  • 参与者与用例之间
  • 用例与用例之间如果存在交互
  • 如果用例与系统边界之外的非人对象有交互
  • 在相关的业务对象有明显的独立性要求,即它们可能在各自的领域类发展变化,但又希望互不影响

一个好的边界类的特点:

  • 边界类应该有助于提高系统的可用性
  • 边界类应该尽可能地保持在较高的层次(如概念层次)上
  • 边界类应该合理封装介于系统与主角之间的交互
  • 如果主角改变他们为系统提供输入的方式,边界类就应该是唯一需求改变的对象
  • 如果系统改变为主角提供输出的方式,边界类就应该是唯一需要改变的对象
  • 边界类必须要知道其他对象类型的需求,以便他们能够得以实施,并相对于系统内部元素保持其可用性和有效性

    控制类

    控制类用于对一个或几个用例所特有的控制行为进行建模。在边界类访问实体类,实体类访问其他实体类时,建议使用控制类。

    实体类

    实体类用于对必须存储的信息和相关行为建模的类。实体对象用于保存和更新一些现象的有关信息。从架构角度上来说实体类位于数据持久层。

1.7 设计类

设计类是系统实施中一个或多个对象的抽象,设计类所对应的对象取决于实施语言。设计类用于设计模型中,它直接使用与编程语言相同的语言来描述。

plantUML写类

@startuml
'直接可以使用class的定义方式写
class Dummy {
  String data
  void methods()
}

class Flight {
   flightNumber : Integer
   departureTime : Date
   int fun1()
   String fun2()
}

note "这是注释" as N2
Flight .. N2

Dummy <|--- Flight

@enduml

结果:
image

1.8 关系

关联关系

关联关系用一条直线表示,描述不同对象之间的结构关系。关联关系是描述对象之间静态的,天然的结构。

' A 关联B
@startuml
class A {
}

class B {
}

A -- B
@enduml

image

依赖关系

依赖关系是用一条带箭头的虚线表示,描述一个对象在运行期会使用到另一个对象的关系。

@startuml
' B 依赖 A
class A {
}

class B {
}

A <.. B
@enduml

image

扩展关系

扩展关系是用一条带箭头的虚线加版型« extends »来表示的。它特别用于在用例模型中说明基本用例中的某个扩展点插入扩展用例。

@startuml
' B 扩展出A
usecase A
usecase B

A <.. B:<<extends>>
@enduml

image 如打电话时,如果通话过程中收到另一个呼叫,我们可以将当前通话保留而接听另一个通话。在这个场景中,保留通话用例就是打电话用例的一个扩展用例

包含关系

包含关系用一条带箭头的虚线加版型« include »表示。它特别用于用例模型,说明在执行基本用例的用例实例过程中插入的行为段。包含用例总是带有抽象性质的,基本用例控制与包含用例的关系,并可依赖于执行包含用例所的结果。包含用例表示的是必需,而不是可选。它应当在概念用例模型中,通过分析业务用例场景而抽象出关键的必选的核心业务而形成包含用例。

@startuml
' A 包含B
usecase A
usecase B

A ..> B:<<include>>
@enduml

image

实现关系

实现关系用带空心箭头的虚线表示。它特别用于在用例模型中连接用例和用例实现,说明基本用例的一个实现方式。 比如缴纳电话费业务的例子,缴纳电话费是一个业务目标,实现途径可能有营业厅缴费,银行缴费,预存话费等,每个用例实现都是同一个业务目标的不同实现过程。

@startuml
usecase 缴纳电话费
usecase 营业厅缴费
usecase  银行缴费
usecase 预存话费

缴纳电话费 <|.. 营业厅缴费
缴纳电话费 <|.. 银行缴费
缴纳电话费 <|.. 预存话费

@enduml

image

精化关系

精华关系是用一条带箭头的虚线加版型« refine »表示的。它特别用于用例模型,一个基本用例可以分解出许多跟小的关键精化用例,这些更小的精化用例更细致的展示了基本用例的核心业务。精化关系仅仅用于建模阶段。

@startuml
usecase 预存话费
usecase 开立账户
usecase 存入现金
usecase 转账
usecase 支付划帐

预存话费 <.. 开立账户:<<refine>>
预存话费 <.. 存入现金:<<refine>>
预存话费 <.. 转账:<<refine>>
预存话费 <.. 支付划帐:<<refine>>
@enduml

image

范化关系

范化关系是一条带空心箭头的直线表示。说明两个对象之间的继承关系。不建议在用例之间使用范化关系。

@startuml
' A 继承自B
class A {
}

class B {
}

A --|> B
@enduml

image

聚合关系

聚合关系是用一条带空心菱形箭头的直线表示的。聚合关系用于类图,特别用于表示实体对象之间的关系,表达整体由部分构成的语义。整体和部分不是强依赖的,即使整体不存在了,部分依然可以存在。

@startuml
' A聚合到B上,或者说B由A组成
class A {
}

class B {
}

A --o B
@enduml

image

组合关系

组合关系用一条带实心菱形箭头的直线表示。组合关系用于类图,特别用于表示实体对象关系,表达整体拥有部分的语义。比如母公司拥有许多子公司。组合关系是一种强依赖的特殊聚合关系。如母公司解体了,子公司也将不存在了。

@startuml
' A组合成B, B由A构成
class A {
}

class B {
}

A --* B
@enduml

image

]]>
国内外安全上市公司 2018-11-02T20:10:30+00:00 luckpiky http://luckpiky.github.io/sec_cop2 当前市值

image

营业收入

image

营业收入增长率

image

毛利率

image

企业规模

image

人均产值

image

人均毛利

image

]]>
开发一个简单的radius服务器 2018-10-14T22:40:30+00:00 luckpiky http://luckpiky.github.io/radius 背景

做基于radius的双因子认证,但未找到一个方便测试的radius服务器,网上找了freeradius,折腾了很久没有搞定,想了想是否可以自己写一个,只要有一个radius的雏形,发一个challenge应该是比较简单的。

pyrad

找到了pyrad,这个是提供一个radius的基本操作的库,提供了客户端和服务端的库,试了一下服务端,windows上起不来,于是只能从头写。
基本思路是自己创建一个udp socket,进行监听,收到报文后,使用pyrad进行解析,然后做身份验证的处理,再用pyrad创建回应消息。
代码如下:

from pyrad import *
import socket
import pyrad.host
import random

BUFSIZE = 1024
KEY = b"testing123"
CHALLENGE = "test2"

class RadiusServer(pyrad.host.Host):
    def __init__(self):
        dict = pyrad.dictionary.Dictionary("dictionary.rfc2865") #从freeradius中搞一个通用的字典使用
        pyrad.host.Host.__init__(self, dict=dict)

    def get_challenge(self): #产生一个4字节的随机挑战码
        challenge = ""
        challenge = challenge + str(chr(random.randint(65,90)))
        challenge = challenge + str(chr(random.randint(65,90)))
        challenge = challenge + str(chr(random.randint(65,90)))
        challenge = challenge + str(chr(random.randint(65,90)))
        return challenge

    def check_pass(self, radpkt): #检查用户名密码,这里简单处理一下,如果密码是test则发起挑战,如果输入正确的挑战码,回应正确,否则失败
        global CHALLENGE
        print("check user")
        if radpkt.PwCrypt(CHALLENGE) == radpkt["User-Password"][0]:
            radpkt.code = packet.AccessAccept
            CHALLENGE = self.get_challenge() #挑战码使用过后就更换掉
            print("AccessAccept")
        elif radpkt.PwCrypt("test") == radpkt["User-Password"][0]:
            radpkt.code = packet.AccessChallenge
            CHALLENGE = self.get_challenge()
            radpkt.AddAttribute("Reply-Message", CHALLENGE) #把挑战码发给客户端
            print("AccessChallenge, please input", CHALLENGE)
        else:
            radpkt.code = packet.AccessReject
            print("AccessReject")

    def get_pkt(self, pkt):
        get_pw = None
        get_name = None
        radpkt = self.CreateAuthPacket(packet=pkt) #解析请求报文
        print("code:", radpkt.code)
        print("authenticator:", radpkt.authenticator)
        print("id:", radpkt.id)
        #radpkt.code = packet.AccessChallenge
        radpkt.secret = KEY

        for key in radpkt.keys():
            print(key, radpkt[key])
            if key == "User-Password":
                get_pw = 1
            if key == "User-Name":
                get_name = 1
        
        if 1 == get_pw and 1 == get_name:
            self.check_pass(radpkt)

        return radpkt.ReplyPacket()

ip_port = ('', 1812)
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # udp协议
server.bind(ip_port)


while True:
    data,client_addr = server.recvfrom(BUFSIZE)
    srv = RadiusServer()
    reply = srv.get_pkt(data)
    server.sendto(reply, client_addr)

测试,采用freeradius中自带的ratest进行测试。

使用第一次验证用的密码进行请求:  
C:\FreeRADIUS.net\bin>radclient.exe -d ..\etc\raddb -f radtest.txt -x -s 127.1 auth testing123
Sending Access-Request of id 172 to 127.0.0.1 port 1812
        User-Name = "admin"
        User-Password = "test"
        NAS-IP-Address = 127.0.0.1
        NAS-Port = 123
rad_recv: Access-Challenge packet from host 127.0.0.1:1812, id=172, length=63
        User-Name = "admin"
        User-Password = "test"
        NAS-IP-Address = 127.0.0.1
        NAS-Port = 123
        Reply-Message = "PFJT"

           Total approved auths:  1
             Total denied auths:  0
               Total lost auths:  0
PS C:\FreeRADIUS.net\bin> .\radtest.bat

收到了应答,里面带了挑战码

#使用挑战码去请求:  
C:\FreeRADIUS.net\bin>radclient.exe -d ..\etc\raddb -f radtest.txt -x -s 127.1 auth testing123
Sending Access-Request of id 0 to 127.0.0.1 port 1812
        User-Name = "admin"
        User-Password = "PFJT"
        NAS-IP-Address = 127.0.0.1
        NAS-Port = 123
rad_recv: Access-Accept packet from host 127.0.0.1:1812, id=0, length=57
        User-Name = "admin"
        User-Password = "PFJT"
        NAS-IP-Address = 127.0.0.1
        NAS-Port = 123

           Total approved auths:  1
             Total denied auths:  0
               Total lost auths:  0
PS C:\FreeRADIUS.net\bin> .\radtest.bat


第二次使用挑战码请求,挑战码已经失效,应该请求失败:
C:\FreeRADIUS.net\bin>radclient.exe -d ..\etc\raddb -f radtest.txt -x -s 127.1 auth testing123
Sending Access-Request of id 196 to 127.0.0.1 port 1812
        User-Name = "admin"
        User-Password = "PFJT"
        NAS-IP-Address = 127.0.0.1
        NAS-Port = 123
rad_recv: Access-Reject packet from host 127.0.0.1:1812, id=196, length=57
        User-Name = "admin"
        User-Password = "PFJT"
        NAS-IP-Address = 127.0.0.1
        NAS-Port = 123

           Total approved auths:  0
             Total denied auths:  1
               Total lost auths:  0

再改进一下,回应的报文中少点请求的属性。

from pyrad import *
import socket
import pyrad.host
import random

BUFSIZE = 1024
KEY = b"testing123"
CHALLENGE = "test2"

class RadiusServer(pyrad.host.Host):
    def __init__(self):
        dict = pyrad.dictionary.Dictionary("dictionary.rfc2865") #从freeradius中搞一个通用的字典使用
        pyrad.host.Host.__init__(self, dict=dict)

    def get_challenge(self): #产生一个4字节的随机挑战码
        challenge = ""
        challenge = challenge + str(chr(random.randint(65,90)))
        challenge = challenge + str(chr(random.randint(65,90)))
        challenge = challenge + str(chr(random.randint(65,90)))
        challenge = challenge + str(chr(random.randint(65,90)))
        return challenge

    def check_pass(self, radpkt, get_pw): #检查用户名密码,这里简单处理一下,如果密码是test则发起挑战,如果输入正确的挑战码,回应正确,否则失败
        global CHALLENGE
        print("check user")
        pwd = ""
		
        if 1 == get_pw:
            pwd = radpkt["User-Password"][0]	
			
            if radpkt.PwCrypt(CHALLENGE) == pwd:
                radpkt.code = packet.AccessAccept
                CHALLENGE = self.get_challenge() #挑战码使用过后就更换掉
                print("AccessAccept")
            elif radpkt.PwCrypt("test") == pwd:
                radpkt.code = packet.AccessChallenge
                CHALLENGE = self.get_challenge()
                radpkt.AddAttribute("Reply-Message", CHALLENGE) #把挑战码发给客户端
                print("AccessChallenge, please input", CHALLENGE)
            else:
                radpkt.code = packet.AccessReject
                print("AccessReject")
					
    def get_pkt(self, pkt):
        get_pw = None
        get_name = None
        radpkt = self.CreateAuthPacket(packet=pkt) #解析请求报文
        print("code:", radpkt.code)
        print("authenticator:", radpkt.authenticator)
        print("id:", radpkt.id)
        #radpkt.code = packet.AccessChallenge
        radpkt.secret = KEY

        for key in radpkt.keys():
            print(key, radpkt[key])
            if key == "User-Password":
                get_pw = 1
            if key == "User-Name":
                get_name = 1
        
        if 1 == get_pw and 1 == get_name:
            self.check_pass(radpkt, get_pw)
			
        reply = radpkt.CreateReply()
		
        for key in radpkt.keys():
            if key == "User-Name":
                reply.AddAttribute("User-Name", radpkt["User-Name"][0])

            if key == "Reply-Message":
                reply.AddAttribute("Reply-Message", radpkt["Reply-Message"][0])

            if key == "NAS-IP-Address":
                reply.AddAttribute("NAS-IP-Address", radpkt["NAS-IP-Address"][0])

            #reply.source = radpkt.source
            reply.code = radpkt.code

        return reply.ReplyPacket()

ip_port = ('', 1812)
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # udp协议
server.bind(ip_port)


while True:
    data,client_addr = server.recvfrom(BUFSIZE)
    srv = RadiusServer()
    reply = srv.get_pkt(data)
    server.sendto(reply, client_addr)

测试:

PS C:\FreeRADIUS.net\bin> .\radclient.exe -d ..\etc\raddb -f radtest.txt -x -s 127.0.0.1 auth testing123
Sending Access-Request of id 48 to 127.0.0.1 port 1812
        User-Name = "admin"
        User-Password = "test"
        NAS-IP-Address = 127.0.0.1
        NAS-Port = 123
rad_recv: Access-Challenge packet from host 127.0.0.1:1812, id=48, length=39
        User-Name = "admin"
        NAS-IP-Address = 127.0.0.1
        Reply-Message = "UTJG"

           Total approved auths:  1
             Total denied auths:  0
               Total lost auths:  0
PS C:\FreeRADIUS.net\bin> .\radclient.exe -d ..\etc\raddb -f radtest.txt -x -s 127.0.0.1 auth testing123
Sending Access-Request of id 228 to 127.0.0.1 port 1812
        User-Name = "admin"
        User-Password = "UTJG"
        NAS-IP-Address = 127.0.0.1
        NAS-Port = 123
rad_recv: Access-Accept packet from host 127.0.0.1:1812, id=228, length=33
        User-Name = "admin"
        NAS-IP-Address = 127.0.0.1

           Total approved auths:  1
             Total denied auths:  0
               Total lost auths:  0
]]>
openldap 2018-10-02T21:15:30+00:00 luckpiky http://luckpiky.github.io/openldap 背景

第一次接触ldap时,是大三实习的那个暑假,使用java访问AD服务器中的用户属性,已经基本忘记如何搞了。这次本来是想用go语言和ldap写一个获取AD服务器中用户的安全组的例子,想测试一下可以达到多高的性能。但是无奈go语言下没有好的ldap库(只找到github中的一个go-ldap,不确定是否好–https://github.com/go-ldap/ldap,后面可以再试试)。 考虑到ldap操作,最出名的应该就是openldap,于是想先试试这个。

下载代码,编译

到http://www.openldap.org/software/download/OpenLDAP/openldap-release/ 下载最新的版本,lib的说明文档:http://www.openldap.org/software/man.cgi?query=ldap ,我下的是openldap-2.4.46.tgz,刚一开始下错了,下了2.4.9版本,一编译直接报错,上网查了说要用2.4.15以后的版本,于是就再次下载。 configure时,还会报缺失BDB的错误,由于不需要SLAPD,所以就–enable-sldap=no给跳过了。

./configure --enable-slapd=no
make depend
make
make install

上网随便找了一个代码,编译试试,果然可以。

gcc t1.c -L /usr/local/lib/ -lldap -llber

测试性能:测试环境,老电脑AMD A10 6700,12G DDR3,hyper-V安装windows2012,并配置好域控;docker启动centos,在centos上编译基于openldap的测试代码。 测试代码如下:

#include <stdio.h>
#include <ldap.h>
#include <sys/time.h>


void print_result(LDAP* ld, LDAPMessage *e)
{
    BerElement *ber;
    char *a;
    char **vals;
    int i;

    for ( a = ldap_first_attribute( ld, e, &ber ); a != NULL; a = ldap_next_attribute( ld, e, ber ) )
    {
        if ((vals = ldap_get_values( ld, e, a)) != NULL )
        {
            for ( i = 0; vals[i] != NULL; i++ )
            {
                //printf( "%s: %s\n", a, vals[i] );

                if (0 == strcmp("memberOf", a))
                {
                    printf("get team:%s\n", vals[i]);
                }
            }
            //printf("print one val end\n");
            ldap_value_free( vals );
        }
        ldap_memfree( a );
    }

    if ( ber != NULL )
    {
        ber_free( ber, 0 );
    }

    //printf("print end\n");
}

void search_ext(LDAP *ld, char *finddn, char *filter)
{
    int rc;
    int msg;
    LDAPMessage *result, *e;
    int finish = 0;
    struct timeval tm = {0};
    tm.tv_sec = -1;

    rc = ldap_search_ext(ld, finddn, LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL, NULL, &tm, &msg);
    if (rc != LDAP_SUCCESS)
    {
        fprintf(stderr, "ldap_search_ext_s: rc: %d, %s\n", rc, ldap_err2string(rc));
        return( 1 );
    }

    //printf("ldap_search_ext success\n");

    int r = ldap_result(ld, msg, LDAP_MSG_ONE, NULL, &result);
    if (r > 0)
    {
        for (e = ldap_first_message(ld, result); e != NULL; e = ldap_next_message(ld, result))
        {
            print_result(ld, e);
        }
    }

    //printf("search_ext end\n");

    return;

}


int main()
{
    LDAP *ld;
    #define HOSTNAME "192.168.1.110"
    #define PORT_NUMBER 389
    #define FIND_DN "dc=test,dc=com"

    LDAPMessage *result, *e;
    BerElement *ber;
    char *a;
    char **vals;
    int i, rc;
    int i_version = 3;
    struct timeval tm = {0};
    tm.tv_sec = -1;

    ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &i_version);
    ldap_set_option(NULL, LDAP_OPT_REFERRALS, LDAP_OPT_ON);

    time_t t1 = time(NULL);
    for (i = 0; i < 1000; i++)
    {
        if ( (ld = ldap_init( HOSTNAME, PORT_NUMBER )) == NULL )
        {  
            perror( "ldap_init" );
            return( 1 );
        }
        //printf( "ldap_init success\n" );

        rc = ldap_simple_bind_s( ld, "cn=administrator,cn=Users,dc=test,dc=com", "Yanhong001");
        if ( rc != LDAP_SUCCESS )
        {
            fprintf(stderr, "ldap_simple_bind_s: rc: %d, %s\n", rc, ldap_err2string(rc));
            return( 1 );
        }

        // printf( "ldap_simple_bind_s success\n" );

        search_ext(ld, "dc=test, dc=com", "(&(objectclass=person)(sAMAccountName=user001))");

        //printf("searchexe end:%d\n", i);

        ldap_unbind_ext(ld, NULL, NULL);
    }
    time_t t2 = time(NULL);

    printf("time:%d\n", t2-t1);

    return 0;
}

测试结果:不加打印,执行1000次认证和search为6秒,加上打印为8秒;如果只做认证,不做search,为5秒。说明这里还有优化的余地。其中只认证一次,然后再循环search,执行第二次search时会卡在ldap_result上一段时间,通过抓包看,AD服务器每次在请求后,马上会回应,但是客户端会一直在发送zeroWindow和KeepAlive消息,估计是openldap中自己在做等待,而不是服务器响应的问题。

]]>
Fortinet资料链接 2018-08-19T23:34:30+00:00 luckpiky http://luckpiky.github.io/fortinet FortiOS Handbook
https://docs.fortinet.com/fortigate/admin-guides

Fortigate演示 账号:demo demo https://www.fortigate.com/ng/prompt?viewOnly&redir=%2Fng%2F

]]>
云端的抗DDOS技术 2018-05-06T21:30:30+00:00 luckpiky http://luckpiky.github.io/ddos DDOS高防IP

image

这个是华为官方给出的产品介绍中的图,从中可以看到业务流量首先是需要引到抗D节点中,然后通过目的IP地址转发到实际的业务节点上(实际业务节点看到的源IP应该是被改为抗D节点中的IP)。流量在经过抗D节点时,节点中的抗D设备对流量及进行清洗。 基本的思路是拿大带宽来抗D,经过清洗后,到达实际业务节点时,大部分攻击流量应该已经被清洗掉了。

阿里的基本思路也是一模一样,见:https://help.aliyun.com/knowledge_detail/40541.html

引流的方式:Web业务需要用户将DNS(Domain Name Server)域名指向该IP,非Web业务需要将业务IP变更为高防IP。

信息通知:当域名受到DDoS攻击时,如果用户需要接收提醒消息(短信或Email),可以设置告警通知。

超过弹性带宽后的处理:若攻击峰值大于客户选定的弹性防护带宽,则客户的高防IP将被拉进黑洞,即客户无法使用该高防IP对外提供服务。

华为最低价格,一个月一个电信IP,10G防护带宽:4600 (BGP 8700)
阿里最低价格,一个月一个BGP IP,10G防护带宽:8800

游戏盾

image

游戏盾与前面的高防IP不同,高防IP是静态的,且靠带宽硬扛。游戏盾通过分布式的抗D节点,将黑客的攻击进行有效的拆分和调度,使得攻击无法集中到某一个点上。同时基于SDK端数据、流量数据,可以通过动态的调度策略将黑客隔离。

image 采用分层处理机制。分为用户层(应该指加密通信),网络层(应该指分组内IP的调度),接入层(对流量的分析和抗D),业务层(通过云平台与真实的业务服务器隔离,从客户端是看不到真是服务器的)。

典型的接入场景
image

]]>
SD-WAN相关的文章 2018-04-23T01:20:30+00:00 luckpiky http://luckpiky.github.io/sdwan SD-WAN技术分析
https://www.sdnlab.com/17810.html

再谈SD-WAN的几种典型部署和实践
https://www.sdnlab.com/20720.html

主流商用SD-WAN方案真的算是SDN吗
https://www.sdnlab.com/20466.html

为什么企业需要SD-WAN
https://www.sdnlab.com/19550.html

业界领先的SD-WAN厂商2017年Q1营收状况
https://www.sdnlab.com/19395.html

重视服务兼顾安全,VeloCloud发布SD-WAN安全架构
https://www.sdnlab.com/19213.html

那些让人怦然心动的SD-WAN功能
https://www.sdnlab.com/20536.html
https://www.sdnlab.com/20589.html

SD-WAN那些事
https://www.sdnlab.com/20554.html
https://www.sdnlab.com/20576.html

关于SD-WAN,你不得不了解的10个常识
https://www.sdnlab.com/20698.html
SD-WAN的节费效果:SD-WAN相较MPLS,每年至少可节省30%的成本投资
由于SD-WAN部署使得添加新的分支站点变得更加容易,因此具有分布式分支站点的垂直行业是最先使用SD-WAN的一些行业,包括零售,制造业,医疗保健,餐馆和金融服务。

重新理解广义SD-WAN——当下解决网络发展的方法论哲学
https://www.sdnlab.com/20424.html

SD-WAN和云专线浅见
https://www.sdnlab.com/19820.html

国内最全最新SD-WAN应用/案例
https://www.sdnlab.com/22389.html

SD-WAN来了,分支路由器就不要了?
https://www.sdnlab.com/22367.html

几个厂商的uCPE
Juniper Networks推出了多款uCPE产品,其中较为突出的是NFX150和NFX250。作为一个安全的通用客户端设备(uCPE)设备,NFX150可以支持和管理多个Juniper和第三方VNF。与Juniper Contrail Service Orchestration一起使用时,它可以提供全自动,安全的SD-WAN,安全路由器和云CPE解决方案,以便经济高效地部署多种业务和基于应用程序的服务,并且易于使用一种GUI管理工具进行管理。Juniper NFX250 uCPE网络服务平台被澳大利亚电信服务提供商Telstra所采用,Telstra使用NFX250平台来运行VeloCloud SD-WAN服务,实现虚拟方式提供托管服务,该uCPE平台提供了一个带开放API的可编程接口,以支持第三方功能的运行。

Dell EMC今年三月发布了开放式通用客户端设备(uCPE)平台,旨在支持多种虚拟网络功能(VNF)和软件定义广域网(SD-WAN)的用例。英特尔至强D-2100处理器为Dell EMC的虚拟边缘平台提供了支持,Dell EMC表示,使用新型英特尔片上系统处理器可以为SD-WAN和uCPE提供更高的性能和更低的延迟。该模块化平台经过了三个SD-WAN厂商的预先验证,分别是:Silver Peak、VMware旗下的VeloCloud以及Versa公司。

]]>
2017年网络安全TOP50 2018-04-22T11:20:30+00:00 luckpiky http://luckpiky.github.io/2017_sec_cop 本文内容来自:http://www.askci.com/news/chanye/20180105/143209115429.shtml

image

(注:本排名由企业规模、影响力、创新力和发展潜力四大指标体系综合评比产生,在某项指标上非常高的企业,并不一定在综合排名上处于很高的位置。)

相对于之前的数据,前30中,科来上升12名,知道创宇上升9名,360、安天上升3名,任子行上升2名,启明、新华三、恒安、蓝盾、北信源、东软、信安上升1名。启明、360、华为保持前三。

创新前十强

image

增长率十强

image

最具发展潜力初创企业(定义为D轮融资以前,且成立不超过6年的企业)

image image image image image image image image image

网络安全上市公司

image

部分公司业务

  • 华为
    主要业务领域:防火墙&NGFW、入侵检测(IDS)/入侵防御(IPS)、统一威胁管理(UTM)、抗DDoS设备、上网行为管理、VPN、Web应用防火墙(WAF)、云抗D、云WAF、移动终端安全、威胁情报、大数据安全、APT。
  • 启明星辰
    主要业务领域:防火墙&NGFW、网络隔离(网闸)、入侵检测(IDS)/入侵防御(IPS)、统一威胁管理(UTM)、数据库安全、数据防泄密DLP、VPN、漏洞扫描、SOC&NGSOC、评估加固&安全运维。
  • 深信服
    主要业务领域:防火墙&NGFW、统一威胁管理(UTM)、上网行为管理、VPN、移动终端安全。
  • 绿盟科技
    主要业务领域:防火墙&NGFW、入侵检测(IDS)/入侵防御(IPS)、统一威胁管理(UTM)、主机防护及自适应、抗DDoS设备、数据库安全、漏洞扫描、Web应用扫描与监控、Web应用防火墙(WAF)、安全咨询服务、评估加固&安全运维。
  • 360企业安全
    主要业务领域:防火墙&NGFW、网络隔离(网闸)、统一威胁管理(UTM)、终端防护&防病毒、终端检测响应(EDR)、VPN、代码审计、Web应用防火墙(WAF)、移动APP安全、移动终端安全、威胁情报、大数据安全、APT、SOC&NGSOC、渗透测试服务。
  • 亚信安全
    主要业务领域:统一威胁管理(UTM)、主机防护及自适应、终端防护&防病毒、终端检测响应EDR、防垃圾邮件、云基础架构安全、移动终端安全、APT、反钓鱼、SOC&NGSOC。
  • 天融信
    主要业务领域:防火墙&NGFW、入侵检测(IDS)/入侵防御(IPS)、数据防泄密(DLP)、VPN、云基础架构安全、大数据安全。
  • 卫士通
    主要业务领域:防火墙&NGFW、入侵检测(IDS)/入侵防御(IPS)、VPN、磁盘加密、文档安全、加密机。
  • 新华三
    主要业务领域:防火墙&NGFW、入侵检测(IDS)/入侵防御(IPS)、统一威胁管理(UTM)、VPN。
  • 安恒信息
    主要业务领域:数据库安全、Web应用扫描与监控、Web应用防火墙(WAF)、堡垒机/运维安全、大数据安全、等保工具。
  • 美亚柏科
    主要业务领域:大数据安全、安全取证、舆情监控、安全培训教育、安全集成服务。
  • 山石网科
    主要业务领域:防火墙&NGFW、入侵检测(IDS)/入侵防御(IPS)、统一威胁管理(UTM)、Web应用防火墙(WAF)、云基础架构安全。
  • 梆梆安全
    主要业务领域:移动app安全、移动网络安全、移动安全测评、移动安全SOC。
  • 安天
    主要业务领域:主机防护、终端防护&防病毒、移动app安全、威胁情报、APT。
  • 恒安嘉新
    主要业务领域:移动与虚拟化安全、安全管理、移动app安全、大数据安全、威胁情报、人工智能等。
  • 蓝盾股份
    主要业务领域:防火墙&NGFW、入侵检测(IDS)/入侵防御(IPS)、安全集成服务。
  • 北信源
    主要业务领域:网络准入(NAC)、终端防护&防病毒、数据防泄密(DLP)、移动终端安全。
  • 迪普科技
    主要业务领域:防火墙&NGFW、入侵检测(IDS)/入侵防御(IPS)、统一威胁管理(UTM)、抗DDoS设备。
  • 通付盾
    主要业务领域:云身份管理、移动app安全、反欺诈。
  • 飞天诚信
    主要业务领域:身份管理。
  • 立思辰
    主要业务领域:加密机、工控安全。
  • 北京CA
    主要业务领域:CA数字证书。
  • 明朝万达
    主要业务领域:文档安全、磁盘加密、移动终端安全。
  • 爱加密
    主要业务领域:移动app安全、移动应用安全管理、移动大数据安全、物联网安全。
  • 任子行
    主要业务领域:上网行为管理、舆情监控。
  • 知道创宇
    主要业务领域:Web应用扫描与监控、云抗D、云WAF、大数据安全。
  • 东软
    主要业务领域:防火墙&NGFW、入侵检测(IDS)/入侵防御(IPS)、Web应用防火墙(WAF)、SOC&NGSOC。
  • 中新网安
    主要业务领域:防火墙&NGFW、抗DDoS设备、Web应用防火墙(WAF)、云抗D、APT。
  • 信安世纪
    主要业务领域:VPN、堡垒机/运维安全、CA数字证书、身份管理。
  • 永信至诚
    主要业务领域:攻防训练平台、安全培训教育。
  • 泰岳安全
    主要业务领域:堡垒机/运维安全、SOC&NGSOC、安全集成服务。
  • 格尔软件
    主要业务领域:CA数字证书。
  • 众人科技
    主要业务领域:身份管理、云身份管理、移动业务安全、大数据安全、安全集成服务。
  • 圣博润
    主要业务领域:终端防护、堡垒机/运维安全、等保工具。
  • 上海观安
    主要业务领域:大数据安全,安全大数据。
  • 中兴通讯
    主要业务领域:安全集成服务。
  • 锐捷网络
    主要业务领域:NGFW、上网行为管理、大数据安全分析平台、WAF、网站安全。
  • 优炫软件
    主要业务领域:文档安全、数据库安全。
  • 交大捷普
    主要业务领域:防火墙&NGFW、数据库安全、Web应用防火墙(WAF)、SOC&NGSOC、评估加固&安全运维。
  • 上讯信息
    主要业务领域:网络准入(NAC)、堡垒机/运维安全、数据库安全、磁盘加密、移动终端安全、安全集成服务。
  • 科来
    主要业务领域:APT、安全取证、安全培训教育。
  • 安赛科技
    主要业务领域:Web应用扫描与监控、Web应用防火墙(WAF)。
]]>