Linux C 函数内赋值char *数组

Publish: February 25, 2019 Category: C/C++ No Comments

编码 test.c: 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

// return赋值
char **char_set_v0(int *n)
{
    char **p = (char **)malloc(3 * sizeof(char *));
    if (p == NULL) {
        printf("out of mem\n");
        return NULL;
    }
    int i =0;
    for (i=0; i<3; i++) {
        *(p+i) = (char *)malloc(1024);
        if (*(p+i) == NULL) {
            printf("out of mem\n");
            return NULL;
        }
        snprintf(*(p+i), 1023, "hello %d", i+1);
    }
    *n = 3;
    
    return p;
}

// 参数赋值
int char_set_v1(char ***m)
{
    char **p = (char **)malloc(3 * sizeof(char *));
    if (p == NULL) {
        printf("out of mem\n");
        return 0;
    }
    int i = 0;
    for (i=0; i<3; i++) {
        p[i] = (char *)malloc(1024);
        if (p[i] == NULL) {
            printf("out of mem\n");
            return 0;
        }
        snprintf(p[i], 1023, "hello %d", i+1);
    }
    *m = p;
    return i;
}


int main(int argc, char **argv)
{
    int i = 0;
    int ret = 0;
    printf(" ---- 0 ------\n");
    char **p = NULL;
    p = char_set_v0(&ret);
    if (p != NULL) {
        for(i=0; i<ret; i++) {
            printf("[%d] %s\n", i, *(p+i));
        }
        for(i=0; i<ret; i++) {
            if (*(p+i) != NULL) {
                free(*(p+i));
                *(p+i) = NULL;
            }
        }
        free(p);
        p = NULL;
    }
    
    printf(" ---- 1 ------\n");
    char **m = NULL;
    ret = char_set_v1(&m);
    if (m != NULL) {
        for(i=0; i<ret; i++) {
            printf("[%d] %s\n", i, m[i]);
        }
        for(i=0; i<ret; i++) {
            if (m[i] != NULL) {
                free(m[i]);
                m[i] = NULL;
            }
        }
        free(m);
        m = NULL;
    }
    return 1;
}

编译: 

gcc -g -o test test.c



使用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