使用openssl做md5,sha,hmac加密

Publish: January 17, 2019 Category: C/C++ No Comments

依赖openssl, 所以确保安装了openssl-devel库

ctcrypt.c:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#include <openssl/conf.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>

/*
 * @param in_str        需要md5的源字符串
 * @param in_str_len    源字符串的长度
 * @param out_str       保存md5后的字符串buf
 * @param out_str_size  out_str的空间长度
 *
 * @return 0 成功  1 失败
 */
int ct_md5(char *in_str, size_t in_str_len, char *out_str, size_t out_str_size)
{
    MD5_CTX         hash_ctx;
    unsigned char   hash_ret[16];
    int             i;
    if (*in_str == '\0' || in_str_len == 0) {
        return 1;
    }
    // initialize a hash context
    if (MD5_Init(&hash_ctx) == 0) {
        return 1;
    }
    // update the input string to the hash context (you can update
    // more string to the hash context)
    if (MD5_Update(&hash_ctx, in_str, in_str_len) == 0) {
        return 1;
    }
    
    // compute the hash result
    if (MD5_Final(hash_ret, &hash_ctx) == 0) {
        return 1;
    }
    char *pout = out_str;
    int pout_len = 0;
    for (i=0; i<32; i++) {
        if (i%2 == 0) {
            pout_len += snprintf(pout+pout_len, out_str_size - pout_len, "%x", (hash_ret[i/2] >> 4) & 0xf);
        } else {
            pout_len += snprintf(pout+pout_len, out_str_size - pout_len, "%x", (hash_ret[i/2]) & 0xf);
        }
    }
    return 0;
}


/*
 * sha1加密
 * @param in_str        需要sha1的源字符串
 * @param in_str_len    源字符串的长度
 * @param out_str       保存sha1后的字符串buf
 * @param out_str_size  out_str的空间长度
 *
 * @return 0 成功  1 失败
 */
int ct_sha1(char *in_str, size_t in_str_len, char *out_str, size_t out_str_size)
{
    SHA_CTX         hash_ctx;
    unsigned char   hash_ret[20];
    int             i;
    // initialize a hash context
    if (SHA1_Init(&hash_ctx) == 0) {
        return 1;
    }
    if (SHA1_Update(&hash_ctx, in_str, in_str_len) == 0) {
        return 1;
    }
    if (SHA1_Final(hash_ret, &hash_ctx) == 0) {
        return 1;
    }
    
    char *pout = out_str;
    int pout_len = 0;
    for (i=0; i<40; i++) {
        if (i%2 == 0) {
            pout_len += snprintf(pout+pout_len, out_str_size - pout_len, "%x", (hash_ret[i/2] >> 4) & 0xf);
        } else {
            pout_len += snprintf(pout+pout_len, out_str_size - pout_len, "%x", (hash_ret[i/2]) & 0xf);
        }
    }
    return 0;
}


/*
 * hash_hmac加密
 * @param algo          使用hash的算法: 
 *                          md2, md4, md5, sha, sha1, dss, dss1, ecdsa, sha224,
 *                          sha256, sha384, sha512, ripemd160, whirlpool
 * @param data          被hash的数据
 * @param data_len      被hash的数据的长度
 * @param key           Shared secret key used for generating the HMAC variant of the message digest.
 * @param key_len       key的长度
 * @param out_data      保存hash后的字符串buf
 * @param out_data_size out_data的空间长度
 *
 * @return 0 成功  1 失败
 */
int ct_hash_hmac(char *algo, char *data, size_t data_len, char *key, size_t key_len, char *out_data, size_t out_data_size)
{
    HMAC_CTX        *hash_ctx;
    unsigned char   hash_ret[20];
    unsigned int    hash_ret_len = 20;
    int             i;
    const EVP_MD    *evp_md;
    
    if (algo == NULL) {
        evp_md = EVP_md_null();
    } else if (strcasecmp(algo, "md2") == 0) {
        evp_md = EVP_md2();
    } else if (strcasecmp(algo, "md4") == 0) {
        evp_md = EVP_md4();
    } else if (strcasecmp(algo, "md5") == 0) {
        evp_md = EVP_md5();
    } else if (strcasecmp(algo, "sha") == 0) {
        evp_md = EVP_sha();
    } else if (strcasecmp(algo, "sha1") == 0) {
        evp_md = EVP_sha1();
    } else if (strcasecmp(algo, "dss") == 0) {
        evp_md = EVP_dss();
    } else if (strcasecmp(algo, "dss1") == 0) {
        evp_md = EVP_dss1();
    } else if (strcasecmp(algo, "ecdsa") == 0) {
        evp_md = EVP_ecdsa();
    } else if (strcasecmp(algo, "sha224") == 0) {
        evp_md = EVP_sha224();
    } else if (strcasecmp(algo, "sha256") == 0) {
        evp_md = EVP_sha256();
    } else if (strcasecmp(algo, "sha384") == 0) {
        evp_md = EVP_sha384();
    } else if (strcasecmp(algo, "sha512") == 0) {
        evp_md = EVP_sha512();
    } else if (strcasecmp(algo, "ripemd160") == 0) {
        evp_md = EVP_ripemd160();
    } else if (strcasecmp(algo, "whirlpool") == 0) {
        evp_md = EVP_whirlpool();
    }
    
    hash_ctx = HMAC_CTX_new();
    HMAC_CTX_init(hash_ctx);
    
    if (HMAC_Init_ex(hash_ctx, key, key_len, evp_md, NULL) == 0) {
        return 1;
    }
    
    if (HMAC_Update(hash_ctx, (unsigned char *)data, data_len) == 0) {
        return 1;
    }
    
    if (HMAC_Final(hash_ctx, hash_ret, &hash_ret_len) == 0) {
        return 1;
    }
    
    HMAC_CTX_cleanup(hash_ctx);
    HMAC_CTX_free(hash_ctx);
    
    char *pout = out_data;
    int pout_len = 0;
    for (i=0; i<hash_ret_len; i++) {
        pout_len += snprintf(pout+pout_len, out_data_size - pout_len, "%02x", (unsigned int)hash_ret[i]);
    }
    return 0;
}


ctcrypt.h:

#ifndef _CTCRYPT_H_
#define _CTCRYPT_H_
/*
 * md5加密
 * @param in_str        需要md5的源字符串
 * @param in_str_len    源字符串的长度
 * @param out_str       保存md5后的字符串buf
 * @param out_str_size  out_str的空间长度
 *
 * @return 0 成功  1 失败
 */
int ct_md5(char *in_str, size_t in_str_len, char *out_str, size_t out_str_size);


/*
 * sha1加密
 * @param in_str        需要sha1的源字符串
 * @param in_str_len    源字符串的长度
 * @param out_str       保存sha1后的字符串buf
 * @param out_str_size  out_str的空间长度
 *
 * @return 0 成功  1 失败
 */
int ct_sha1(char *in_str, size_t in_str_len, char *out_str, size_t out_str_size);


/*
 * hash_hmac加密
 * @param algo          使用hash的算法: 
 *                          md2, md4, md5, sha, sha1, dss, dss1, ecdsa, sha224,
 *                          sha256, sha384, sha512, ripemd160, whirlpool
 * @param data          被hash的数据
 * @param data_len      被hash的数据的长度
 * @param key           Shared secret key used for generating the HMAC variant of the message digest.
 * @param key_len       key的长度
 * @param out_data      保存hash后的字符串buf
 * @param out_data_size out_data的空间长度
 *
 * @return 0 成功  1 失败
 */
int ct_hash_hmac(char *algo, char *data, size_t data_len, char *key, size_t key_len, char *out_data, size_t out_data_size);

#endif


sample.c:

/*
 * 使用方法:
 *  # gcc -g -o sample sample.c -I/usr/include/ -L/usr/lib64/ -lcrypto
 *  # ./sample 'hello, world'
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "ctcrypt.h"
int main(int argc, char **argv)
{
    char out_string[1024] = {0};
    int ret = 0;
    
    ret = ct_md5(argv[1], strlen(argv[1]), out_string, sizeof(out_string));
    printf("md5     ret[%d]:%s\n", ret, out_string);
    
    memset(out_string, 0, sizeof(out_string));
    ret = ct_sha1(argv[1], strlen(argv[1]), out_string, sizeof(out_string));
    printf("sha1    ret[%d]:%s\n", ret, out_string);
    
    memset(out_string, 0, sizeof(out_string));
    char key[] = "012345678";
    ret = ct_hash_hmac("sha1", argv[1], strlen(argv[1]), key, strlen(key), out_string, sizeof(out_string));
    printf("hmac    ret[%d]:%s\n", ret, out_string);
    
    return 0;
    
}


解决僵死进程defunct方式

Publish: September 14, 2017 Category: C/C++ No Comments

这里有以下几种方式解决僵死进程:

忽略子进程退出信号

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <strings.h>

void sig_catch( int sig, void (*f) () )
{
    struct sigaction sa;
    sa.sa_handler = f;
    sa.sa_flags   = 0;
    sigemptyset(&sa.sa_mask);
    sigaction(sig, &sa, (struct sigaction *) 0);
}

sig_catch(SIGCHLD, SIG_IGN);


2. 父进程wait子进程退出信号

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <strings.h>

#define wait_crashed(w) ((w) & 127)
#define wait_exitcode(w) ((w) >> 8)

int wait_pid(int *wstat, int pid)
{
    int r;
    do
        r = waitpid(pid, wstat, 0);
    while ((r == -1) && (errno == EINTR));
    return r;
}


int main(int argc, char **argv) 
{
    pid_t   pid;

    switch (pid = fork()) {
        case -1:
            printf("fork error\n");
            return 1;
        case 0:
            printf("child[%d]: exit\n");
            _exit(31);
    }

    int wstat;
    int exitcode;

    int pid_w = wait_pid(&wstat, pid);
    if (pid_w != pid) {
        printf("waitpid fail. pid_w[%lld] pid[%lld]\n", pid_w, pid);
        return 1;
    }
    
    if (wait_crashed(wstat)) {
        printf("waitpid crashed \n");
        return 1;
    }
    
    exitcode = wait_exitcode(wstat);
    printf("child[%lld] exit code[%d]\n", pid, exitcode);

    return 0;
}


3. Catch子进程信号,并在处理函数内wait (注意这个函数里不可以做syslog调用,在大量并发时会程序异常)

// 此函数内不可调用syslog
void sigchld_exit()
{
    int exitcode;
    int wstat, pid;
    while ((pid = waitpid(-1, &wstat, WNOHANG)) > 0) {
        //log_info("child[%ld] exit[%d]", pid, wstat);
        if (wait_crashed(wstat)) {
            //log_error("child[%ld] crashed[%d]", pid, wstat);
            return;
        }   
        exitcode = wait_exitcode(wstat);
        //log_info("child[%ld] exitcode[%d]", pid, exitcode);
    }   
}

{
    sig_catch(SIGCHLD, sigchld_exit);
}

inetd,xinetd守护进程

Publish: August 31, 2017 Category: Shell,C/C++ No Comments

    inetd, xinetd 为每一个服务都建立侦听socket,当inetd,xinetd收到连接时,它首先确定该socket协议和端口所对应的服务程序,然后fork子进程来启动exec该服务程序,并把连接的通信socket描述符作为标准输入、标准输出和标准错误输出描述符传递给子进程。

    这种方式,不但不需要为每个服务启动独立的服务程序,而且在设计这些服务程序时,还可以避免考虑复杂的socket编程,所需的仅仅是简单的字符串分析。比如telnet、ftp等,都是通过 inetd, xinetd 守护进程来实现的。


xinetd:

  1. 设置文件: /etc/services

# 在文件尾添加一行:
sj    20227/tcp

此文件记录了 xinetd 守护的每个服务的基本信息,包括名称、端口号和协议类型。此例添加了服务 sj 占用TCP协议的20227端口。


  2. 设置文件: /etc/xinetd.d/sj

service sj
{
    socket_type = stream
    protocol    = tcp 
    wait        = no
    user        = root
    only_from   = 198.72.5.0 localhost  
    banner      = /usr/home/grayson/deny_banner
    server      = /usr/home/grayson/sj_server
}

这是一个常见的服务配置文件。我们逐行看一下:

  • 第一行指定这是一个服务并给服务取一个名称。

  • socket_type 描述连接如何工作,常常是 stream(用于 TCP 连接)或 dgram(用于 UDP 服务)。

  • wait 控制 xinetd 是每次处理一个连接 (wait=yes),还是每次处理多个连接 (wait=no)。

  • user 指定守护进程应该作为哪个用户运行。这个用户常常是根用户(超级用户),但是某些服务最好或必须作为服务的创建者运行。

  • only_from 指定哪些系统可以对这个服务发出请求。在这里,只允许 198.72.5 子网上的系统和本地主机使用 sj 服务。最右边的 0 作为通配符;允许 IP 地址前缀为 198.72.5 的任何系统请求服务。可以使用多种表示法指定系统;详情参见 xinetd.conf 手册页。(输入 man 5 xinetd.conf。)如果允许所有就去掉这行!

  • 如果禁止访问,就把 /usr/local/etc/deny_banner 文件的内容发送给客户机。

  • 最后,server 指定允许访问时运行的可执行程序。


  3. 重启服务:

service xinetd restart

    

  4. 服务: sj_server

#include <stdio.h>
int main()
{
    char buf[1024] = {0};
    printf("Welcome!\n");
    fflush(stdout);
    while (1) {
        fgets(buf, sizeof(buf), stdin);     // 读取请求
        printf("Input: %s", buf);           // 返回应答
        fflush(stdout);
        if (strncmp(buf, "exit", 4) == 0)
            break;
    }
    return 0;
}

  5. 编译

# gcc -g -o sj_server sj_server.c


SSR 服务器端安装(单用户版)

Publish: November 7, 2016 Category: Shell,C/C++ No Comments

基本库安装

centos:

yum install python-setuptools && easy_install pip
yum install git

ubuntu/debian:

apt-get install python-pip
apt-get install git

获取源代码

git clone -b manyuser https://github.com/breakwa11/shadowsocks.git

执行完毕后此目录会新建一个shadowsocks目录,基本根目录的是多用户版(即数据库版),子目录中的是单用户版

  • 根目录即: ./shadowsocks

  • 子目录即: ./shadowsocks/shadowsocks


服务端配置

添加用户配置文件: /etc/shadowsocks-rss/user-config.json

{
    "server": "0.0.0.0",
    "server_ipv6": "::",
    "server_port": 8388,
    "local_address": "127.0.0.1",
    "local_port": 1080,
    "password": "123qwe",
    "timeout": 120,
    "udp_timeout": 60,
    "method": "aes-256-cfb",
    "protocol": "origin",
    "protocol_param": "",
    "obfs": "http_simple_compatible",
    "obfs_param": "",
    "dns_ipv6": false,
    "connect_verbose_info": 0,
    "redirect": "",
    "fast_open": false
}

启动 (一定要先进入单用户版本)

# cd shadowsocks/shadowsocks
# python server.py -c /etc/shadowsocks-rss/user-config.json


使用pipe进行进程间通讯

Publish: September 23, 2016 Category: C/C++ No Comments

    使用pipe进行进程间通讯,pipe只能在共同血缘(父子进程)进程中使用。

一、父子进程间通讯:

/*
 * 双向管道流模式: 父子进程互相读写
 * 
 *    --------      ---------------     --------
 *   | 父进程  |    |    UNIX 内核  |    | 子进程  |
 *   |        |    |===============|    |        |
 *   | pfd1[1]|--->|输入  管道I  输出|--->| pfd1[0]|
 *   |        |    |===============|    |        |
 *   | pfd2[0]|<---|输出  管道II 输入|<---| pfd2[1]|
 *   |        |    |               |    |        |
 *    --------      ---------------      --------
 *    
 */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
    int pfd1[2], pfd2[2];
    pid_t pid;
    if (pipe(pfd1) < 0) {
        fprintf(stderr, "pipe fail:%s\n", strerror(errno));
        exit(0);
    }
    if (pipe(pfd2) < 0) {
        fprintf(stderr, "pipe fail:%s\n", strerror(errno));
        exit(0);
    }
    if ((pid = fork()) < 0) {
        fprintf(stderr, "fork fail:%s\n", strerror(errno));
        exit(0);
    }
    if (pid == 0) {
        // ------ 子进程 ------
        close(pfd1[1]);
        close(pfd2[0]);
        char buf[1024] = {0};
        int r = 0;
        while (r = read(pfd1[0], buf, sizeof(buf))) {
            if (r > 0) {
                printf("  [child_%ld]:Read %d:%s", getpid(), r, buf);
                if (strcasecmp(buf, "quit\n") == 0) {
                    r = snprintf(buf, sizeof(buf), "Bye Bye.\n");
                    r = write(pfd2[1], buf, r);
                    printf("  [child_%ld]: %ld:%s", getpid(), r, buf);
                    return 0;
                }
                memset(buf, 0, sizeof(buf));
                r = snprintf(buf, sizeof(buf), "250 OK\n");
                r = write(pfd2[1], buf, r);
                printf("  [child_%ld]:Write %ld:%s", getpid(), r, buf);
                memset(buf, 0, sizeof(buf));
            }
        }
        return 0;
    }
    // ------ 父进程 ------
    char buf[1024] = {0};
    int r = 0;
    close(pfd1[0]);
    close(pfd2[1]);
    while (fgets(buf, sizeof(buf), stdin) != NULL) {
        printf("[parent_%ld]:Read %d:%s", getpid(), strlen(buf), buf);
        r = write(pfd1[1], buf, strlen(buf));
        printf("[parent_%ld]:Write %d bytes, Succ\n", getpid(), r);
        memset(buf, 0, sizeof(buf));
        r = read(pfd2[0], buf, sizeof(buf));
        printf("[parent_%ld]:Read: %d:%s", getpid(), r, buf);
        if (strcasecmp(buf, "Bye Bye.\n") == 0) {
            printf("[parent_%ld]:Read: %d:%s", getpid(), r, buf);
            return 0;
        }
        memset(buf, 0, sizeof(buf));
    }
    return 0;
}


二、连接标准I/O的管道流模式:  子进程使用标准I/O文件描述符进行通讯

    pipe_parent.c:

/*
 * 连接标准I/O的管道流模式: 父子进程互相读写
 * 
 *    --------            -----------------          ---------------
 *   | 父进程 |          |    UNIX 内核    |        | 子进程        |
 *   |        |          |=================|        |               |
 *   | pfd1[1]| -------> |输入  管道I  输出| ------>| 0 <==>pfd1[0] |
 *   |        |          |=================|        |               |
 *   | pfd2[0]| <------- |输出  管道II 输入| <------| 1 <==>pfd2[1] |
 *   |        |          |                 |        |               |
 *    --------            -----------------          ---------------
 *    
 */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
char *(pipe_child[]) = { "./pipe_child", 0};
int main(int argc, char **argv)
{
    int pfd1[2], pfd2[2];
    pid_t pid;
    if (pipe(pfd1) < 0) {
        fprintf(stderr, "pipe fail:%s\n", strerror(errno));
        exit(0);
    }
    if (pipe(pfd2) < 0) {
        fprintf(stderr, "pipe fail:%s\n", strerror(errno));
        exit(0);
    }
    if ((pid = fork()) < 0) {
        fprintf(stderr, "fork fail:%s\n", strerror(errno));
        exit(0);
    }
    if (pid == 0) {
        // ------ 子进程 ------
        if (dup2(pfd1[0], 0) < 0) {
        fprintf(stderr, "child dup2 fail:%s\n", strerror(errno));
        _exit(1);
        }
        if (dup2(pfd2[1], 1) < 0) {
            fprintf(stderr, "child dup2 fail:%s\n", strerror(errno));
            _exit(1);
        }
        close(pfd1[1]);
        close(pfd2[0]);
        int ret = execvp(*pipe_child, pipe_child);
        if (ret < 0) {
            fprintf(stderr, "child execvp:%s fail:%s\n", pipe_child, strerror(errno));
            _exit(1);
        }
        return 0;
    }
    // ------ 父进程 ------
    close(pfd1[0]);
    close(pfd2[1]);
    char buf[1024] = {0};
    int r = 0;
    while (fgets(buf, sizeof(buf), stdin) != NULL) {
        printf("[parent_%ld]:Read %d:%s", getpid(), strlen(buf), buf);
        r = write(pfd1[1], buf, strlen(buf));
        printf("[parent_%ld]:Write %d bytes, Succ\n", getpid(), r);
        memset(buf, 0, sizeof(buf));
        r = read(pfd2[0], buf, sizeof(buf));
        printf("[parent_%ld]:Read: %d:%s", getpid(), r, buf);
        if (strcasecmp(buf, "Bye Bye.\n") == 0) {
            printf("[parent_%ld]:Read: %d:%s", getpid(), r, buf);
            return 0;
        }
        memset(buf, 0, sizeof(buf));
    }
    return 0;
}


    pipe_child.c:

/*
 * 连接标准I/O的管道流模式: 父子进程互相读写
 * 
 *    --------            -----------------          ---------------
 *   | 父进程 |          |    UNIX 内核    |        | 子进程        |
 *   |        |          |=================|        |               |
 *   | pfd1[1]| -------> |输入  管道I  输出| ------>| 0 <==>pfd1[0] |
 *   |        |          |=================|        |               |
 *   | pfd2[0]| <------- |输出  管道II 输入| <------| 1 <==>pfd2[1] |
 *   |        |          |                 |        |               |
 *    --------            -----------------          ---------------
 *    
 */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
    char buf[1024] = {0};
    int r = 0;
    while (r = read(0, buf, sizeof(buf))) {
        if (r > 0) {
            fprintf(stderr, "  [child_%ld]:Read %d:%s", getpid(), r, buf);
            if (strcasecmp(buf, "quit\n") == 0) {
                r = snprintf(buf, sizeof(buf), "Bye Bye.\n");
                r = write(1, buf, r);
                fprintf(stderr, "  [child_%ld]: %ld:%s", getpid(), r, buf);
                return 0;
            }
            memset(buf, 0, sizeof(buf));
            r = snprintf(buf, sizeof(buf), "250 OK\n");
            r = write(1, buf, r);
            fprintf(stderr, "  [child_%ld]:Write %ld:%s", getpid(), r, buf);
            memset(buf, 0, sizeof(buf));
        }
    }
    return 0;
}