apportate/src/main.c

364 lines
8.0 KiB
C
Raw Normal View History

2022-11-24 08:18:05 +00:00
#include "headers.h"
enum
{
/* display a status bar */
/* 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 */
/* all verbose output
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 */
/* 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
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)
{
2023-02-19 20:44:19 +00:00
int i, translen, redirnum, gotheader;
int sockd;
2022-12-13 17:12:07 +00:00
char *recvbufp;
char *sendbufp, *offsetp, *errstr;
2022-12-13 17:12:07 +00:00
char *urip;
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)
{
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
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 */
if(!param[Q])
{
param[V]++;
}
break;
default:
2023-02-19 20:44:19 +00:00
goto usage;
}
}
2022-12-13 17:12:07 +00:00
urip = argv[optind];
2022-12-13 17:12:07 +00:00
for(redirnum = 0; redirnum < REDIR_LIM;)
{
2022-12-13 17:12:07 +00:00
start:
if(NULL != tlsc)
{
tls_free(tlsc);
tlsc = NULL;
}
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-13 17:12:07 +00:00
if(filed)
{
fclose(filed);
filed = NULL;
}
2022-12-13 17:12:07 +00:00
if(sendbufp)
{
free(sendbufp);
sendbufp = NULL;
}
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;
}
2023-02-19 20:44:19 +00:00
if(param[V] >= 2)
{
2022-12-13 17:12:07 +00:00
fprintf(stderr, "URI parsed, results follow...\nProtocol: %s\nFQDN: %s\nPath: %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\nlength of fqdn: %lu\nlength of path: %lu\n", strlen(uristruct.proto), strlen(uristruct.fqdn), strlen(uristruct.path));
}
}
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)))
{
2023-02-19 20:44:19 +00:00
fprintf(stderr, "couldn't generate request. Unknown protocol: %s?\n", uristruct.proto);
exit(-1);
}
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-13 17:12:07 +00:00
outpath = "default";
}
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)))
{
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)
{
2023-02-19 20:44:19 +00:00
tls_reset(tlsc);
}
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);
}
2023-02-19 20:44:19 +00:00
if(param[V] >= 3)
{
2022-12-13 17:12:07 +00:00
fprintf(stderr, "sent: %d bytes\n", i);
}
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-13 17:12:07 +00:00
exit(EXIT_SUCCESS);
2022-12-13 17:12:07 +00:00
usage:
fprintf(stderr, "usage: %s [-qvb] [-o file] uri\n", argv[0]);
exit(EXIT_FAILURE);
err:
fprintf(stderr, "%s: %s\n", argv[0], errstr);
exit(EXIT_FAILURE);
}
2023-02-19 20:44:19 +00:00