You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
4.2 KiB
181 lines
4.2 KiB
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <gnutls/gnutls.h>
|
|
#include <gnutls/crypto.h>
|
|
// https://abf.io/import/cgilib
|
|
#include <cgi.h>
|
|
// https://abf.io/import/fcgi
|
|
#include <fcgios.h>
|
|
#include <fcgi_stdio.h>
|
|
|
|
// 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;
|
|
}
|
|
|