#include #include #include #include #include #include #include #include #include // https://abf.io/import/cgilib #include // https://abf.io/import/fcgi #include #include // allow to redefine as gcc -D... #ifndef DIR #define DIR "/tmp" #endif #define HTTP_OK 200 #define HTTP_BAD_REQUEST 400 #define HTTP_ERROR 500 /* Here we could verify validity of a IPv4 or IPv6 address, * but that is very complex. Just sanitize input. */ int _verify_ip(char *ip){ int rc = 0; for (int i = 0; i < strlen(ip); i++){ if (!( (ip[i] == '.') || /* IPv4 dots */ (ip[i] == ':') || /* IPv6 */ (ip[i] == '/') || /* XXX Is it needed for IPv6? */ (isalnum(ip[i]) != 0) /* Numbers (v4 and v6) and letters (v6) */ )) { rc = 1; break; } } return rc; } int _verify_geometry(char *value){ int rc = 0; for (int i = 0; i < strlen(value); i++){ if (isdigit(value[i]) == 0) { rc = 1; break; } } return rc; } /* Generate a random alphanum sequence * Example: 10841ee0cc4f (12 chars) */ int _rand(char **rs){ // https://books.google.ru/books?id=1nG3BwAAQBAJ // hex.data will be 6x2=12 chars unsigned char buf_rnd[6]; gnutls_datum_t rnd = { buf_rnd, sizeof(buf_rnd) }; int rc; rc = gnutls_rnd(GNUTLS_RND_NONCE, rnd.data, rnd.size); if (rc != GNUTLS_E_SUCCESS) { printf("Error in gnutls_rnd()"); goto out; } gnutls_datum_t hex; rc = gnutls_hex_encode2(&rnd, &hex); if (rc != GNUTLS_E_SUCCESS) { printf("Error in gnutls_hex_encode2()"); goto out; } *rs = malloc(hex.size); rc = sprintf(*rs, "%s", hex.data); if (rc < 0) { printf("Error in sprintf()"); goto out; } out: return rc; } int main(){ int rc = 0; int http_code = HTTP_ERROR; // text of errors which must be seen by people char *error_msg; if (access(DIR, W_OK) != 0) { fprintf(stderr, "Directory %s does not exist or is not writable\n", DIR); goto out; } // first chdir() and then chroot()! if (!( chdir(DIR) == 0 && chroot(DIR) == 0 )) { fprintf(stderr, "Cannot chdir and chroot into %s\n", DIR); goto out; } s_cgi *cgi; while(FCGI_Accept() >= 0) { // cgiReadVariables() returns NULL if QUESRY_STRING is empty, // and it is empty when neither width not height are defined, // so checking its return value is useless. cgi = cgiInit(); char *ip = getenv("REMOTE_ADDR"); if (ip == NULL) { fprintf(stderr, "%s\n", "Env REMOTE_ADDR is not set"); http_code = HTTP_ERROR; goto fcgi_out; } rc = _verify_ip(ip); if (rc != 0) { fprintf(stderr, "%s\n", "Incorrect REMOTE_ADDR"); http_code = HTTP_BAD_REQUEST; goto fcgi_out; } char *cgi_width = cgiGetValue(cgi, "width"); char *cgi_height = cgiGetValue(cgi, "height"); int bal = 0; if ( (cgi_width != NULL) && (_verify_geometry(cgi_width) == 0) ) bal++; if ( (cgi_height != NULL) && (_verify_geometry(cgi_height) == 0) ) bal++; if (bal == 1) { error_msg = "Define both width and height"; http_code = HTTP_BAD_REQUEST; goto fcgi_out; } char *hex; int max_try = 10; for (int i = 1; i <= max_try; i++) { if (i == max_try) { fprintf(stderr, "%s\n", "Error in random file loop"); http_code = HTTP_ERROR; goto fcgi_out; } rc = _rand(&hex); if (rc < 0) { fprintf(stderr, "Error in _rand()"); continue; } if (access(hex, F_OK) != 0) { break; } } FCGI_FILE *d = fopen(hex, "w"); if (d == NULL) { fprintf(stderr, "Error openning file %s/%s for writing\n", DIR, hex); http_code = HTTP_ERROR; goto fcgi_out; } fprintf(d, "ip=%s\n", ip); if (bal == 2) { fprintf(d, "width=%s\n", cgi_width); fprintf(d, "height=%s\n", cgi_height); } rc = fclose(d); if (rc != 0) { fprintf(stderr, "Error closing file %s/%s\n", DIR, hex); http_code = HTTP_ERROR; goto fcgi_out; } http_code = HTTP_OK; fcgi_out: cgiFree(cgi); printf("Content-Type: text/plain; charset=utf-8\r\n"); printf("Status: %d\r\n", http_code); printf("\r\n", http_code); if (http_code == HTTP_OK) { printf("%s\r\n", "OK"); rc = 0; } else { if (error_msg) printf("ERROR: %s\r\n", error_msg); rc = 1; } } out: return rc; }