CANopenSocket CANopenCGI.c hacking

/****************************************************************************************
 *                      CANopenSocket CANopenCGI.c hacking
 * 说明:
 *     分析一下CANopenSocket中的CANopenCGI部分是怎么工作的。
 *
 *                                          2017-3-23 深圳 南山平山村 曾剑锋
 ***************************************************************************************/

/*
 * Client socket command interface (Apache CGI) for CANopenSocket.
 *
 * @file        CANopenCGI.c
 * @author      Janez Paternoster
 * @copyright   2016 Janez Paternoster
 *
 * This file is part of CANopenNode, an opensource CANopen Stack.
 * Project home page is <https://github.com/CANopenNode/CANopenNode>.
 * For more information on CANopen see <http://www.can-cia.org/>.
 *
 * CANopenNode is free and open source software: you can redistribute
 * it and/or modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <fnmatch.h>
#include <sys/un.h>
#include <sys/socket.h>


#ifndef BUF_SIZE
#define BUF_SIZE            100000
#endif


/* Helper functions */
static void errExitErrno(char* msg) {
    printf("%s: %s\n", msg, strerror(errno));
    exit(EXIT_FAILURE);
}

static void errExit(char* msg) {
    printf("%s\n", msg);
    exit(EXIT_FAILURE);
}

/**
 * 字符串拷贝并转换成大写
 */
static void strcpyToUpper(char *dest, const char *src) {
    char in;

    do {
        in = *(src++);
        *(dest++) = toupper(in);
    } while(in != 0);
}

/**
 * 字符串转换成大写
 */
static void strToUpper(char *str) {
    char c;

    do {
        c = *(str);
        *(str++) = toupper(c);
    } while(c != 0);
}

/**
 * 字符串转换成小写
 */
static void strToLower(char *str) {
    char c;

    do {
        c = *(str);
        *(str++) = tolower(c);
    } while(c != 0);
}

/* Decode hex string 'str' of length 'len' and return numerical value.
 * In case of error in string, set 'err' to 1. */
/**
 * 将十六进制的字符串转成数字
 */
static unsigned int hex2dec(const char *str, int len, int *err){
    unsigned int val = 0;
    int i;

    for(i=0; i<len; i++) {
        char c = str[i];
        if(c >= '0' && c <= '9') {
            c = c - '0';
        } else if (c >= 'A' && c <= 'F') {
            c = c - ('A' - 10);
        }
        else {
            *err = 1;
            return 0;
        }
        val = val << 4 | c;
    }
    return val;
}

static void sendCommand(int fd, int sequence, char* command);

static void printUsage(void) {
printf(
"Usage: canopen.cgi?wnniiiissdd=xxxx[&rnniiiissdd=]\n"
"  - w    - One digit - 'W'rite or 'R'ead.\n"
"  - nn   - Two hex digits of node ID.\n"
"  - iiii - Four hex digits of Object Dictionary Index.\n"
"  - ss   - Two hex digits of Object Dictionary Subindex.\n"
"  - dd   - One to three digits of data type.\n"
"  - xxxx - Value to be written.\n"
"\n"
"Datatypes:\n"
"  - b                 - Boolean.\n"
"  - u8, u16, u32, u64 - Unsigned integers.\n"
"  - i8, i16, i32, i64 - Signed integers.\n"
"  - r32, r64          - Real numbers.\n"
"  - t, td             - Time of day, time difference.\n"
"  - vs                - Visible string (between double quotes).\n"
"  - os, us, d         - Octet string, unicode string, domain."
);
}


/******************************************************************************/
int main (int argc, char *argv[], char *env[]) {
    char socketPath[260] = {0};  /* Name of the local domain socket. */

    FILE *fp;
    int fdSocket;
    struct sockaddr_un addr;
    char *queryString;
    int queryStringAllocated = 0;

    /* whitelist and blacklist are arrays of null separated strings, which
     * contains patterns for comparision with commands from query string. */
    char *whitelist;
    char *blacklist;
    int whitelistLen;
    int blacklistLen;


    /* Print mime */
    /**
     * 输出http协议的头
     */
    printf("Content-type:text/plain\n\n");


    /* Get program options from configuration file */
    /**
     * 处理配置文件
     */
    fp = fopen("canopen.conf", "r");
    if(fp == NULL) {
        errExitErrno("Can't open configuration file");
    }
    else {
        const char spaceDelim[] = " \t\n\r\f\v";
        char buf[1000];
        int wlSize = 1000;   /* byte length */
        int blSize = 1000;
        int wlDataSize = 0;
        int blDataSize = 0;

        whitelist = (char *) malloc(wlSize);
        blacklist = (char *) malloc(blSize);;
        // 最开始wlDataSize长度都是0,随着allow检测到的配置越来越多,长度会越来越长
        whitelistLen = 0;   /* number of tokens in list */
        blacklistLen = 0;
        if(whitelist == NULL || blacklist == NULL) {
            errExitErrno("Whitelist or Blacklist can't be allocated.");
        }

        // 每次读取一行
        while(fgets(buf, sizeof(buf), fp) != NULL) {
            char *token;
            token = strtok(buf, spaceDelim);

            if(token == NULL) {

            }
            /**
             * 获取socketPath配置
             */
            else if(strcasecmp(token, "socketPath") == 0) {
                if(strlen(socketPath) != 0) {
                    errExit("Duplicate 'socketPath' in canopen.conf.");
                }
                strncpy(socketPath, strtok(NULL, spaceDelim), sizeof(socketPath));
                socketPath[sizeof(socketPath)-1] = 0;
            }
            else if(strcasecmp(token, "allow") == 0) {
                // 保存上一次的wlDataSize长度,随着allow检测到的配置越来越多,长度会越来越长
                int prevDataSize = wlDataSize;

                // 获取value
                token = strtok(NULL, spaceDelim);
                // 计算value长度并+1,最后一个字节用于存放字符串结束符,这个长度叠加到wlDataSize中
                wlDataSize += (strlen(token) + 1);
                // 长度大于预设字符串长度,双倍扩容并重新分配,不过从这里开看最大也就是双倍的扩容长度
                while(wlDataSize > wlSize) {
                    wlSize *= 2;
                    whitelist = (char *) realloc(whitelist, wlSize);
                    if(whitelist == NULL) {
                        errExitErrno("Whitelist can't be allocated.");
                    }
                }
                // 拷贝当前的匹配数据到whitelist中
                strcpyToUpper(&whitelist[prevDataSize], token);
                whitelistLen ++;
            }
            /**
             * 类是于白名单
             */
            else if(strcasecmp(token, "deny") == 0) {
                int prevDataSize = blDataSize;

                token = strtok(NULL, spaceDelim);
                blDataSize += (strlen(token) + 1);
                while(blDataSize > blSize) {
                    blSize *= 2;
                    blacklist = (char *) realloc(blacklist, blSize);
                    if(blacklist == NULL) {
                        errExitErrno("Blacklist can't be allocated.");
                    }
                }
                strcpyToUpper(&blacklist[prevDataSize], token);
                blacklistLen ++;
            }
        }
    }

    fclose(fp);


    /* Create and connect client socket */
    /**
     * 创建本地socket
     */
    fdSocket = socket(AF_UNIX, SOCK_STREAM, 0);
    if(fdSocket == -1) {
        errExitErrno("Socket creation failed");
    }

    /**
     * 配置本地socket
     */
    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, socketPath, sizeof(addr.sun_path) - 1);

    /**
     * 连接本地socket
     */
    if(connect(fdSocket, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) {
        errExitErrno("Socket connection failed");
    }


    /* get query string */
    /**
     * 获取网络请求数据
     */
    queryString = getenv("QUERY_STRING"); /* HTTP GET method. */
    if(queryString != NULL && strlen(queryString) == 0) {
        queryString = malloc(BUF_SIZE);
        if(queryString == NULL) {
            errExitErrno("queryString can't be allocated.");
        }
        queryStringAllocated = 1;
        fgets(queryString, BUF_SIZE, stdin); /* HTTP POST method. */
    }
    if(queryString == NULL && argc >= 2) {
        queryString = argv[1];  /* If no query string, try first argument. */
    }


    /* get commands from query string */
    /**
     * 解析网络请求数据
     */
    if(queryString != NULL && strlen(queryString) > 0) {
        char *command;
        int sequence = 1;

        /* put whole query string to upper case */
        /**
         * 将请求数据转为大写的格式
         */
        strToUpper(queryString);

        command = strtok(queryString, "&");
        while(command != NULL) {
            int i;
            int offset;
            int passed = 0;

            /* Test whitelist and blacklist */
            /**
             * 一个一个偏移着找
             */
            offset = 0;
            for(i=0; i<whitelistLen; i++) {
                char *patern = &whitelist[offset];
                if(fnmatch(patern, command, 0) == 0) {
                    passed = 1;
                    break;
                }
                offset += strlen(patern) + 1;
            }
            /**
             * 检查黑名单
             */
            if(passed == 1) {
                offset = 0;
                for(i=0; i<blacklistLen; i++) {
                    char *patern = &blacklist[offset];
                    if(fnmatch(patern, command, 0) == 0) {
                        passed = -1; /* not allowed */
                        break;
                    }
                    offset += strlen(patern) + 1;
                }
            }

            /* Send command or error message */
            if(strlen(command) < 9) {
                printf("? %s [%d] ERROR: 101 - Syntax error in command.\n", command, sequence);
            }
            else if(passed == 1) {
                sendCommand(fdSocket, sequence, command);
            }
            else {
                printf("%c %c%c%c%c%c%c%c%c [%d] ERROR: 100 - Access restriction, command %s.\n",
                    command[0], command[1], command[2], command[3], command[4],
                    command[5], command[6], command[7], command[8],
                    sequence, (passed==0)?"not on whitelist":" on blacklist");
            }

            command = strtok(NULL, "&");
            sequence ++;
        }
    }
    else {
        printUsage();
    }


    close(fdSocket);
    free(whitelist);                    // 释放白名单
    free(blacklist);                    // 释放黑名单
    if(queryStringAllocated == 1) {
        free(queryString);
    }

    exit(EXIT_SUCCESS);
}


static void sendCommand(int fd, int sequence, char* command) {
    int i, err;
    char comm;
    unsigned int nodeId, idx, sidx;
    char dataType[4];
    char *value = "";

    char buf[BUF_SIZE];

    /* Parse command. It is at least 8 characters long. */
    /**
     * 解析命令
     */
    err = 0;

    comm = command[0];
    if(comm != 'R' && comm != 'W') {
        err = 1;
    }

    nodeId = hex2dec(&command[1], 2, &err);
    if(nodeId < 1 || nodeId > 127) {
        err = 1;
    }

    idx = hex2dec(&command[3], 4, &err);
    sidx = hex2dec(&command[7], 2, &err);

    for(i=0; i<sizeof(dataType); i++) {
        char c = command[9+i];

        if(c == '=' || c == 0) {
            dataType[i] = 0;
            if(c == '=') {
                value = &command[10+i];
            }
            break;
        }
        dataType[i] = c;
    }
    if(i > 3) {
        err = 1;
        dataType[0] = 0;
    }
    if(strlen(value) > (sizeof(buf) - 50)) {
        err = 1;
    }

    /* Write command according to CiA309-3. */
    /**
     * 命令转换,转换成canopend能接收的命令格式
     */
    if(err == 0) {
        size_t wlen, rlen;

        strToLower(dataType);

        wlen = sprintf(buf, "[%d] 0x%02X %c 0x%04X 0x%02X %s %s\n",
                sequence, nodeId, tolower(comm), idx, sidx, dataType, value);

        if (write(fd, buf, wlen) != wlen) {
            errExit("Socket write failed");
        }

        rlen = read(fd, buf, sizeof(buf));

        if(rlen == -1) {
            errExit("Socket read failed");
        }

        printf("%c %02X%04X%02X %s",
            comm, nodeId, idx, sidx, buf);
    }
    else {
        printf("? %s [%d] ERROR: 101 - Syntax error in command.\n",
                    command, sequence);
    }
}

 

posted on 2017-03-23 13:12  zengjf  阅读(739)  评论(0编辑  收藏  举报

导航