2022-11-24 08:18:05 +00:00
# include "headers.h"
enum
{
/* display a status bar */
2022-12-07 15:41:26 +00:00
/* currently not implemented */
2022-11-24 08:18:05 +00:00
B ,
/* put the file somewhere other than the current directory */
O ,
/* silence all output -- the default */
/* note that if -q is passed explicitly, it will irrevocably override -v and -b */
Q ,
/* increase the verbosity level. higher levels imply you want the content lower levels would provide */
2022-11-30 14:34:31 +00:00
/* all verbose output
2022-12-07 15:41:26 +00:00
is sent to stderr for ease of capture */
2022-11-24 08:18:05 +00:00
/* 0: no informational, statistical or debug output */
/* 1: informational output (Sending request FOO to host BAR, Received response header BAZ, etc) */
/* 2: statistical. informational+size of header and body in bytes, round-trip time, etc */
/* 3+: debug. statistical+all internal state changes */
2022-12-07 15:41:26 +00:00
/* currently we only implement 1 */
2022-11-24 08:18:05 +00:00
V
} ;
2022-12-13 17:12:07 +00:00
enum
{
/* change this to alter how many redirect attempts should be tried */
/* todo: make this a command-line parameter? */
REDIR_LIM = 10
} ;
2022-11-24 08:18:05 +00:00
2022-11-30 14:34:31 +00:00
char param [ 4 ] = { 0 } ;
/* below is to accompany param[O] */
char * outpath ;
2022-11-24 08:18:05 +00:00
int main ( int argc , char * * argv )
2022-11-30 14:34:31 +00:00
{
2023-02-19 20:44:19 +00:00
int i , translen , redirnum , gotheader ;
2022-12-07 15:41:26 +00:00
int sockd ;
2022-12-13 17:12:07 +00:00
char * recvbufp ;
2022-12-07 15:41:26 +00:00
char * sendbufp , * offsetp , * errstr ;
2022-12-13 17:12:07 +00:00
char * urip ;
2022-12-07 15:41:26 +00:00
FILE * filed ;
struct tls * tlsc ;
uri uristruct ;
i = translen = sockd = 0 ;
sendbufp = offsetp = 0 ;
2022-12-13 17:12:07 +00:00
if ( ( recvbufp = malloc ( BUFSIZ + 1 ) ) = = NULL )
2022-12-07 15:41:26 +00:00
{
errstr = " failed to init " ;
goto err ;
}
filed = 0 ;
tlsc = NULL ;
2022-12-13 17:12:07 +00:00
if ( argc = = 1 )
{
goto usage ;
}
2023-02-19 20:44:19 +00:00
2022-12-07 15:41:26 +00:00
for ( i = 0 ; ( i = getopt ( argc , argv , " bo:qv " ) ) ! = - 1 ; i = 0 )
{
switch ( i )
{
case ' b ' :
param [ B ] = 1 ;
break ;
case ' o ' :
param [ O ] = 1 ;
outpath = optarg ;
break ;
case ' q ' :
param [ Q ] = 1 ;
param [ B ] = 0 ;
param [ V ] = 0 ;
break ;
case ' v ' :
2023-02-19 20:44:19 +00:00
/* we handle v differently because we want to support different levels of verbosity */
2022-12-07 15:41:26 +00:00
if ( ! param [ Q ] )
{
param [ V ] + + ;
}
break ;
default :
2023-02-19 20:44:19 +00:00
goto usage ;
2022-12-07 15:41:26 +00:00
}
}
2022-12-13 17:12:07 +00:00
urip = argv [ optind ] ;
2022-12-07 15:41:26 +00:00
2022-12-13 17:12:07 +00:00
for ( redirnum = 0 ; redirnum < REDIR_LIM ; )
2022-12-07 15:41:26 +00:00
{
2022-12-13 17:12:07 +00:00
start :
if ( NULL ! = tlsc )
{
tls_free ( tlsc ) ;
tlsc = NULL ;
}
2022-12-07 15:41:26 +00:00
2022-12-13 17:12:07 +00:00
/* I don't care *what* you say, the system should *never* in a sane context return 0, 1, or 2 as a valid file descriptor */
/* libc reserves these */
if ( 2 < sockd )
{
close ( sockd ) ;
sockd = 0 ;
}
2022-12-07 15:41:26 +00:00
2022-12-13 17:12:07 +00:00
if ( filed )
{
fclose ( filed ) ;
filed = NULL ;
}
2022-11-30 14:34:31 +00:00
2022-12-13 17:12:07 +00:00
if ( sendbufp )
{
free ( sendbufp ) ;
sendbufp = NULL ;
}
2022-11-30 14:34:31 +00:00
2022-12-13 17:12:07 +00:00
/* todo: init uristruct as well */
2023-02-19 20:44:19 +00:00
uristruct . proto = NULL ;
uristruct . fqdn = NULL ;
uristruct . path = NULL ;
2022-12-13 17:12:07 +00:00
if ( uri_parse ( urip , & uristruct ) )
2023-02-19 20:44:19 +00:00
{
errstr = " couldn't parse URI. " ;
goto err ;
}
2022-12-07 15:41:26 +00:00
2023-02-19 20:44:19 +00:00
if ( param [ V ] > = 2 )
2022-12-07 15:41:26 +00:00
{
2022-12-13 17:12:07 +00:00
fprintf ( stderr , " URI parsed, results follow... \n Protocol: %s \n FQDN: %s \n Path: %s \n " , uristruct . proto , uristruct . fqdn , uristruct . path ) ;
2023-02-19 20:44:19 +00:00
if ( param [ V ] > = 3 )
{
fprintf ( stderr , " length of proto: %lu \n length of fqdn: %lu \n length of path: %lu \n " , strlen ( uristruct . proto ) , strlen ( uristruct . fqdn ) , strlen ( uristruct . path ) ) ;
}
2022-12-07 15:41:26 +00:00
}
2022-12-13 17:12:07 +00:00
2023-02-19 20:44:19 +00:00
sendbufp = reqgen ( & uristruct ) ;
2022-12-13 17:12:07 +00:00
/* we should probably modify uri_parse to return a zero value on failure... */
if ( NULL = = ( sendbufp = reqgen ( & uristruct ) ) )
2022-12-07 15:41:26 +00:00
{
2023-02-19 20:44:19 +00:00
fprintf ( stderr , " couldn't generate request. Unknown protocol: %s? \n " , uristruct . proto ) ;
exit ( - 1 ) ;
2022-12-07 15:41:26 +00:00
}
2022-12-13 17:12:07 +00:00
/* if outpath isn't set because we haven't received a -o param, */
/* then we should set the outpath to the final component of the */
/* URI. */
/* If we can't do that, we should just set it to 'default'. */
/* todo: maybe add a check to ensure we don't overwrite? but if */
/* the user tells us to, who are we to question them? */
if ( NULL = = outpath & & ! strlen ( ( outpath = ( 1 + strrchr ( uristruct . path , ' / ' ) ) ) ) )
2022-12-07 15:41:26 +00:00
{
2022-12-13 17:12:07 +00:00
outpath = " default " ;
2022-12-07 15:41:26 +00:00
}
2022-12-13 17:12:07 +00:00
if ( param [ V ] )
{
fprintf ( stderr , " connecting to %s using protocol %s... \n " , uristruct . fqdn , uristruct . proto ) ;
}
2023-02-19 20:44:19 +00:00
/* once again, I'll repeat myself -- while a system *COULD* return 0, 1, or 2, it *SHOULD NEVER DO SO* in a sane environment */
if ( 2 > = ( sockd = dial ( uristruct . fqdn , uristruct . proto ) ) )
2022-12-07 15:41:26 +00:00
{
2023-02-19 20:44:19 +00:00
errstr = " failed to connect " ;
goto err ;
}
/* todo: upgrade this to a more general mechanism */
if ( ! strncmp ( " https " , uristruct . proto , 5 ) )
{
if ( NULL ! = tlsc )
2022-12-07 15:41:26 +00:00
{
2023-02-19 20:44:19 +00:00
tls_reset ( tlsc ) ;
2022-12-07 15:41:26 +00:00
}
2022-12-13 17:12:07 +00:00
2023-02-19 20:44:19 +00:00
struct tls_config * config = tls_config_new ( ) ;
tlsc = tls_client ( ) ;
tls_configure ( tlsc , config ) ;
if ( - 1 = = tls_connect_socket ( tlsc , sockd , uristruct . fqdn ) )
2022-12-13 17:12:07 +00:00
{
2023-02-19 20:44:19 +00:00
errstr = " failed to upgrade connection to use TLS, aborting \n " ;
2022-12-13 17:12:07 +00:00
goto err ;
}
}
2023-02-19 20:44:19 +00:00
if ( param [ V ] > = 2 )
2022-12-13 17:12:07 +00:00
{
2023-02-19 20:44:19 +00:00
fprintf ( stderr , " Sending request... \n -----REQUEST START----- \n %s \n -----REQUEST END----- \n " , sendbufp ) ;
2022-12-13 17:12:07 +00:00
}
if ( NULL ! = tlsc )
{
2023-02-19 20:44:19 +00:00
if ( param [ V ] )
{
fprintf ( stderr , " writing over tls... \n " ) ;
}
if ( - 1 = = ( i = tls_write ( tlsc , sendbufp , strlen ( sendbufp ) ) ) )
{
fprintf ( stderr , " libtls internal error: " ) ;
errstr = ( char * ) tls_error ( tlsc ) ;
goto err ;
}
2022-12-13 17:12:07 +00:00
}
else
{
2023-02-19 20:44:19 +00:00
if ( param [ V ] )
{
fprintf ( stderr , " writing over socket... \n " ) ;
}
2022-12-13 17:12:07 +00:00
i = send ( sockd , sendbufp , strlen ( sendbufp ) , 0 ) ;
}
2022-12-07 15:41:26 +00:00
2023-02-19 20:44:19 +00:00
if ( param [ V ] > = 3 )
2022-12-07 15:41:26 +00:00
{
2022-12-13 17:12:07 +00:00
fprintf ( stderr , " sent: %d bytes \n " , i ) ;
2022-12-07 15:41:26 +00:00
}
2022-12-13 17:12:07 +00:00
/* actual read loop */
2023-02-19 20:44:19 +00:00
2022-12-13 17:12:07 +00:00
for ( gotheader = 0 , translen = 1 ; translen ; memset ( recvbufp , 0 , BUFSIZ + 1 ) )
{
if ( NULL = = tlsc )
{
translen = recv ( sockd , recvbufp , BUFSIZ , 0 ) ;
}
else
{
translen = tls_read ( tlsc , recvbufp , BUFSIZ ) ;
}
2023-02-19 20:44:19 +00:00
if ( param [ V ] > = 3 )
2022-12-13 17:12:07 +00:00
{
fprintf ( stderr , " recv: %d bytes \n " , translen ) ;
}
/* parsing here? */
if ( ! strncmp ( uristruct . proto , " http " , 4 ) & & ! gotheader )
{
switch ( resp_parse_http ( recvbufp ) )
{
case - 1 :
if ( param [ V ] )
{
fprintf ( stderr , " Response header parsing unnecessary, moving on... \n " ) ;
}
break ;
case 200 :
/* by now we have the first transmission from the server that we actually care about */
/* we just need to get the the end of the headres, now that we're done with 'em */
2023-02-19 20:44:19 +00:00
if ( param [ V ] > = 3 )
2022-12-13 17:12:07 +00:00
{
fprintf ( stderr , " 200 OKAY, moving to end of header... \n " ) ;
}
for ( ; NULL = = ( offsetp = strstr ( recvbufp , " \r \n \r \n " ) ) ; )
{
2023-02-19 20:44:19 +00:00
if ( param [ V ] > = 2 )
{
fprintf ( stderr , " Searching to end of header... \n " ) ;
}
2022-12-13 17:12:07 +00:00
if ( NULL ! = tlsc )
{
tls_read ( tlsc , recvbufp , BUFSIZ ) ;
}
else
{
recv ( sockd , recvbufp , BUFSIZ , 0 ) ;
}
}
/* move forward four to get past the delimiter */
/* todo: add error checking in the case of never receiving a delimiter */
offsetp + = 4 ;
gotheader = 1 ;
break ;
/* intentional drop through from 301 to 302 */
case 301 :
case 302 :
2023-02-19 20:44:19 +00:00
urip = http_get_keyval ( " Location " , recvbufp ) ;
2022-12-13 17:12:07 +00:00
if ( param [ V ] )
{
2023-02-19 20:44:19 +00:00
fprintf ( stderr , " Redirecting to %s... \n " , urip ) ;
2022-12-13 17:12:07 +00:00
}
2023-02-19 20:44:19 +00:00
2022-12-13 17:12:07 +00:00
redirnum + + ;
2023-02-19 20:44:19 +00:00
/* could use continue, but structured programming makes it easier to use goto in this circumstance... */
2022-12-13 17:12:07 +00:00
goto start ;
case 400 :
errstr = " 400 Bad Request. Internal apport error? " ;
goto err ;
default :
fprintf ( stdout , " %s " , recvbufp ) ;
errstr = " invalid response from server. " ;
goto err ;
}
}
if ( ! filed & & NULL = = ( filed = fopen ( outpath , " w " ) ) )
{
errstr = " couldn't open file. " ;
goto err ;
}
i = fwrite ( offsetp , sizeof ( char ) , translen - ( offsetp - recvbufp ) , filed ) ;
2023-02-19 20:44:19 +00:00
if ( param [ V ] > = 3 )
2022-12-13 17:12:07 +00:00
{
fprintf ( stderr , " fwrite: %d bytes \n " , i ) ;
}
offsetp = recvbufp ;
}
2023-02-19 20:44:19 +00:00
tls_free ( tlsc ) ;
2022-12-13 17:12:07 +00:00
close ( sockd ) ;
fclose ( filed ) ;
free ( sendbufp ) ;
free ( recvbufp ) ;
break ;
2022-12-07 15:41:26 +00:00
}
2022-12-13 17:12:07 +00:00
2022-12-07 15:41:26 +00:00
exit ( EXIT_SUCCESS ) ;
2022-11-30 14:34:31 +00:00
2022-12-13 17:12:07 +00:00
usage :
fprintf ( stderr , " usage: %s [-qvb] [-o file] uri \n " , argv [ 0 ] ) ;
exit ( EXIT_FAILURE ) ;
2022-12-07 15:41:26 +00:00
err :
fprintf ( stderr , " %s: %s \n " , argv [ 0 ] , errstr ) ;
exit ( EXIT_FAILURE ) ;
}
2023-02-19 20:44:19 +00:00