# 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 : 10841 ee0cc4f ( 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 ;
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 ) {
cgi = cgiInit ( ) ;
if ( cgi = = NULL ) {
fprintf ( stderr , " %s \n " , " Error initializaing CGI " ) ;
http_code = HTTP_ERROR ;
goto fcgi_out ;
}
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 " ) ;
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 ( ( cgi_width ! = NULL ) & & ( _verify_geometry ( cgi_width ) = = 0 ) )
fprintf ( d , " width=%s \n " , cgi_width ) ;
if ( ( cgi_height ! = NULL ) & & ( _verify_geometry ( cgi_height ) = = 0 ) )
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 {
rc = 1 ;
}
}
out :
return rc ;
}