/* * Client socket command interface (Apache CGI) for CANopenSocket. * * @file CANopenCGI.c * @author Janez Paternoster * @copyright 2016 - 2020 Janez Paternoster * * This file is part of CANopenSocket, a Linux implementation of CANopen * stack with master functionality. Project home page is * . CANopenSocket is based * on CANopenNode: . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #ifndef BUF_SIZE #define BUF_SIZE 1000000 #endif /* Helper functions */ static void errExitErrno(char* msg) { printf("%s: %s\n", msg, strerror(errno)); exit(EXIT_FAILURE); } static void errExitErrnoUGID(char* msg) { struct passwd *user; struct group *grp; printf("%s: %s\n", msg, strerror(errno)); user = getpwuid(getuid()); grp = getgrgid(getgid()); printf("Access from user: %s, group: %s\n", (user == NULL) ? NULL : user->pw_name, (grp == NULL) ? NULL : grp->gr_name); 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= '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 */ 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);; 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) { } 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) { int prevDataSize = wlDataSize; token = strtok(NULL, spaceDelim); wlDataSize += (strlen(token) + 1); while(wlDataSize > wlSize) { wlSize *= 2; whitelist = (char *) realloc(whitelist, wlSize); if(whitelist == NULL) { errExitErrno("Whitelist can't be allocated."); } } 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 */ fdSocket = socket(AF_UNIX, SOCK_STREAM, 0); if(fdSocket == -1) { errExitErrno("Socket creation failed"); } memset(&addr, 0, sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, socketPath, sizeof(addr.sun_path) - 1); if(connect(fdSocket, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) { errExitErrnoUGID("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 127) { err = 1; } idx = hex2dec(&command[3], 4, &err); sidx = hex2dec(&command[7], 2, &err); for(i=0; i 3) { err = 1; dataType[0] = 0; } if(strlen(value) > (sizeof(buf) - 50)) { err = 1; } /* Write command according to CiA309-3. */ 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) - 1); if(rlen < 0) { errExit("Socket read failed"); } else { buf[rlen] = 0; } printf("%c %02X%04X%02X %s", comm, nodeId, idx, sidx, buf); } else { printf("? %s [%d] ERROR: 101 - Syntax error in command.\n", command, sequence); } }