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-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
|
|
|
{
|
2022-12-07 15:41:26 +00:00
|
|
|
int i, translen, gotheader;
|
|
|
|
int sockd;
|
|
|
|
char *recvbuf;
|
|
|
|
char *sendbufp, *offsetp, *errstr;
|
|
|
|
FILE *filed;
|
|
|
|
struct tls *tlsc;
|
|
|
|
uri uristruct;
|
|
|
|
|
|
|
|
i = translen = sockd = 0;
|
|
|
|
sendbufp = offsetp = 0;
|
|
|
|
if( (recvbuf = malloc(BUFSIZ+1)) == NULL)
|
|
|
|
{
|
|
|
|
errstr = "failed to init";
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
filed = 0;
|
|
|
|
tlsc = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
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':
|
|
|
|
/* we handle v differently because we want to support different levels of verbosity */
|
|
|
|
if(!param[Q])
|
|
|
|
{
|
|
|
|
param[V]++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
errstr = "[-vb] [-q] [-o <file>] uri";
|
|
|
|
goto usage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* we should probably modify uri_parse to return a zero value on failure... */
|
|
|
|
if(uri_parse(argv[optind], &uristruct))
|
|
|
|
{
|
|
|
|
errstr = "[-q|[-v -b]] [-o <file>] uri";
|
|
|
|
goto usage;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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'. */
|
|
|
|
if(NULL == outpath && !strlen((outpath = (1 + strrchr(uristruct.path, '/')))))
|
|
|
|
{
|
|
|
|
outpath = "default";
|
|
|
|
}
|
|
|
|
|
|
|
|
/* generate our request */
|
|
|
|
if( !(sendbufp = reqgen(&uristruct)))
|
|
|
|
{
|
|
|
|
errstr = "Unknown protocol.";
|
|
|
|
goto err;
|
|
|
|
}
|
2022-11-30 14:34:31 +00:00
|
|
|
|
2022-12-07 15:41:26 +00:00
|
|
|
if(param[V])
|
|
|
|
{
|
|
|
|
fprintf(stderr, "request: %s\n", sendbufp);
|
|
|
|
}
|
2022-11-30 14:34:31 +00:00
|
|
|
|
2022-12-07 15:41:26 +00:00
|
|
|
/* having a routine or a global variable to track whether we need TLS would be */
|
|
|
|
/* nice to add in the future */
|
|
|
|
/* could probably store whether tls is necessary in a char within uristruct... */
|
|
|
|
/* Note: at the moment, if the TLS pointer passed is non-NULL, */
|
|
|
|
/* dial's return code can only be treated as an indicator of success. */
|
2022-11-30 14:34:31 +00:00
|
|
|
|
|
|
|
if(!strcmp("https", uristruct.proto))
|
2022-12-07 15:41:26 +00:00
|
|
|
{
|
|
|
|
sockd = dial(uristruct.fqdn, uristruct.proto, &tlsc);
|
|
|
|
}
|
2022-11-30 14:34:31 +00:00
|
|
|
else
|
2022-12-07 15:41:26 +00:00
|
|
|
{
|
|
|
|
sockd = dial(uristruct.fqdn, uristruct.proto, NULL);
|
|
|
|
}
|
2022-11-24 08:18:05 +00:00
|
|
|
|
2022-12-07 15:41:26 +00:00
|
|
|
if(!sockd)
|
|
|
|
{
|
|
|
|
errstr = "Couldn't connect to the host using the specified protocol.";
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(tlsc)
|
|
|
|
{
|
|
|
|
translen = tls_write(tlsc, sendbufp, strlen(sendbufp));
|
|
|
|
}
|
2022-11-30 14:34:31 +00:00
|
|
|
else
|
2022-12-07 15:41:26 +00:00
|
|
|
{
|
|
|
|
translen = send(sockd, sendbufp, strlen(sendbufp), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(translen)
|
|
|
|
{
|
|
|
|
if(param[V])
|
|
|
|
{
|
|
|
|
printf("send: %d bytes\n", translen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
errstr = "Couldn't transmit data.";
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now for a slightly more complex version of the same routine: until we've encountered the */
|
|
|
|
/* delimiter, "\r\n\r\n", don't write to disk. Once we have, calculate the size of the body, */
|
|
|
|
/* then write that number of bytes to disk starting from the offset. Then, write everything */
|
|
|
|
/* until end of transmission. */
|
|
|
|
for(gotheader = 0, offsetp = NULL, translen = 1; translen > 0; memset(recvbuf, 0, BUFSIZ+1))
|
|
|
|
{
|
|
|
|
if( NULL == tlsc)
|
|
|
|
{
|
|
|
|
printf("recv: %d bytes\n", translen = recv(sockd, recvbuf, BUFSIZ, 0));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printf("recv: %d bytes\n", translen = tls_read(tlsc, recvbuf, BUFSIZ));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if we haven't gotten the header and our delimiter isn't in the */
|
|
|
|
/* received string, we're getting a multipart header and need to */
|
|
|
|
/* skip over it. */
|
|
|
|
/* right now we assume HTTP/S -- this is undesirable... */
|
|
|
|
if( strcmp("gopher", uristruct.proto) && !gotheader)
|
|
|
|
{
|
|
|
|
if( NULL == (offsetp = strstr(recvbuf, "\r\n\r\n")))
|
|
|
|
{
|
|
|
|
printf("continuing...\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gotheader = 1;
|
|
|
|
offsetp += 4;
|
|
|
|
printf("got the header\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !filed && !(filed = fopen(outpath, "w")))
|
|
|
|
{
|
|
|
|
errstr = "couldn't open file";
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
fwrite(offsetp, sizeof(char), (translen - (offsetp - recvbuf)), filed);
|
|
|
|
offsetp = recvbuf;
|
|
|
|
}
|
|
|
|
free(recvbuf);
|
|
|
|
close(sockd);
|
|
|
|
fclose(filed);
|
|
|
|
|
|
|
|
|
|
|
|
exit(EXIT_SUCCESS);
|
2022-11-30 14:34:31 +00:00
|
|
|
|
|
|
|
|
2022-12-07 15:41:26 +00:00
|
|
|
err:
|
|
|
|
fprintf(stderr, "%s: %s\n", argv[0], errstr);
|
|
|
|
exit(EXIT_FAILURE);
|
2022-11-30 14:34:31 +00:00
|
|
|
|
2022-12-07 15:41:26 +00:00
|
|
|
usage:
|
|
|
|
fprintf(stderr, "usage: %s: %s\n", argv[0], errstr);
|
|
|
|
exit(EXIT_FAILURE);
|
2022-11-30 14:34:31 +00:00
|
|
|
|
2022-12-07 15:41:26 +00:00
|
|
|
}
|