This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <libgen.h> | |
#include <limits.h> | |
#include <stdarg.h> | |
#include <string.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <netdb.h> | |
#include <netinet/in.h> | |
#include <errno.h> | |
#include <error.h> | |
#define REMOTE_HOST "localhost" | |
#define REMOTE_PORT "8000" | |
#define BUFSIZE 4096 | |
#define CRLF "\r\n" | |
#define SAVE_GAI_ERROR(func, errno) \ | |
err_func = (func); \ | |
err_file = __FILE__; \ | |
err_line = __LINE__; \ | |
err_errno = 0; \ | |
err_gai_errno = errno; | |
#define SAVE_ERROR(func) \ | |
err_func = (func); \ | |
err_file = __FILE__; \ | |
err_line = __LINE__; \ | |
err_errno = errno; \ | |
err_gai_errno = 0; | |
#define CUR_ERRNO (err_errno || err_gai_errno) | |
const char *prog; | |
const char *err_func; | |
const char *err_file; | |
int err_line; | |
int err_errno; | |
int err_gai_errno; | |
const char* family_to_string(int family) | |
{ | |
return family == AF_INET ? "AF_INET" | |
: family == AF_INET6 ? "AF_INET6" | |
: "<unknown>"; | |
} | |
const char* socktype_to_string(int socktype) | |
{ | |
return socktype == SOCK_STREAM ? "SOCK_STREAM" | |
: socktype == SOCK_DGRAM ? "SOCK_DGRAM" | |
: "<unknown>"; | |
} | |
const char* protocol_to_string(int protocol) | |
{ | |
return protocol == IPPROTO_TCP ? "IPPROTO_TCP" | |
: protocol == IPPROTO_UDP ? "IPPROTO_UDP" | |
: "<unknown>"; | |
} | |
int get_host_serv(struct addrinfo *ai, char **phost, char **pserv) | |
{ | |
int r; | |
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; | |
char *host, *serv; | |
r = getnameinfo(ai->ai_addr, ai->ai_addrlen, | |
hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), | |
NI_NUMERICHOST | NI_NUMERICSERV); | |
if (r) { | |
SAVE_GAI_ERROR("getnameinfo", r); | |
return 1; | |
} | |
host = strdup(hbuf); | |
if ( ! host) { | |
SAVE_ERROR("strdup"); | |
return 1; | |
} | |
serv = strdup(sbuf); | |
if ( ! serv) { | |
SAVE_ERROR("strdup"); | |
free(host); | |
return 1; | |
} | |
*phost = host; | |
*pserv = serv; | |
return 0; | |
} | |
int print_addrinfo(struct addrinfo *ai) | |
{ | |
const char *family = family_to_string(ai->ai_family); | |
const char *socktype = socktype_to_string(ai->ai_socktype); | |
const char *protocol = protocol_to_string(ai->ai_protocol); | |
const char *family2 = family_to_string(ai->ai_addr->sa_family); | |
char *host, *serv; | |
int r; | |
r = get_host_serv(ai, &host, &serv); | |
if (r) { | |
return 1; | |
} | |
printf("flags=%i, family=%s, socktype=%s, protocol=%s, family=%s, host=%s, serv=%s\n", | |
ai->ai_flags, family, socktype, protocol, family2, host, serv); | |
free(host); | |
free(serv); | |
return 0; | |
} | |
int get_socket(int *psfd) | |
{ | |
int r, sfd; | |
struct addrinfo hints; | |
struct addrinfo *result, *ai; | |
memset(&hints, 0, sizeof(struct addrinfo)); | |
// hints.ai_family = AF_INET; | |
hints.ai_socktype = SOCK_STREAM; | |
r = getaddrinfo(REMOTE_HOST, REMOTE_PORT, &hints, &result); | |
if (r) { | |
SAVE_GAI_ERROR("getaddrinfo", r); | |
return 1; | |
} | |
for (ai = result; ai; ai = ai->ai_next) { | |
char *host, *serv; | |
r = get_host_serv(ai, &host, &serv); | |
if (r) { | |
freeaddrinfo(result); | |
return 1; | |
} | |
printf("creating socket for %s:%s...", host, serv); | |
sfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | |
if (sfd == -1) { | |
SAVE_ERROR("socket"); | |
} | |
printf(" %s\n", sfd == -1 ? "fail" : "done"); | |
if (sfd == -1) { | |
free(host); | |
free(serv); | |
freeaddrinfo(result); | |
return 1; | |
} | |
printf("connecting to %s:%s...", host, serv); | |
r = connect(sfd, ai->ai_addr, ai->ai_addrlen); | |
printf(" %s\n", r ? "fail" : "done"); | |
free(host); | |
free(serv); | |
if (r) { | |
close(sfd); | |
} else { | |
*psfd = sfd; | |
freeaddrinfo(result); | |
return 0; | |
} | |
} | |
freeaddrinfo(result); | |
return 1; | |
} | |
int set_prog(char *argv0) | |
{ | |
char *prog1 = strdup(argv0); | |
if ( ! prog1) { | |
prog = argv0; | |
return 1; | |
} | |
char *prog2 = basename(prog1); | |
free(prog1); | |
char *prog3 = strdup(prog2); | |
if ( ! prog3) { | |
prog = argv0; | |
return 1; | |
} | |
prog = prog3; | |
return 0; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
(void)argc; | |
int r, sfd; | |
ssize_t ss; | |
set_prog(argv[0]); | |
r = get_socket(&sfd); | |
if (r) { | |
goto error; | |
} | |
// TODO. add port | |
char request[] = "GET / HTTP/1.0" CRLF | |
"Host: " REMOTE_HOST CRLF | |
CRLF; | |
ss = write(sfd, request, sizeof(request)); | |
if (ss == -1) { | |
SAVE_ERROR("write"); | |
close(sfd); | |
goto error; | |
} else if (ss != sizeof(request)) { | |
error_at_line(1, 0, __FILE__, __LINE__, "write: %li / %lu\n", ss, sizeof(request)); | |
close(sfd); | |
exit(EXIT_FAILURE); | |
} | |
char b[BUFSIZE]; | |
while (1) { | |
ss = read(sfd, b, sizeof(b)); | |
if (ss == -1) { | |
SAVE_ERROR("read"); | |
close(sfd); | |
goto error; | |
} else if (ss == 0) { | |
close(sfd); | |
break; | |
} | |
// printf("%li\n", ss); | |
ssize_t ss2; | |
ss2 = fwrite(b, 1, ss, stdout); | |
if (ss2 != ss) { | |
error_at_line(1, 0, __FILE__, __LINE__, "fwrite: %li / %lu\n", ss2, ss); | |
close(sfd); | |
exit(EXIT_FAILURE); | |
} | |
} | |
exit(EXIT_SUCCESS); | |
error: | |
if (err_errno) | |
error_at_line(1, err_errno, err_file, err_line, "%s", err_func); | |
else if (err_gai_errno) | |
error_at_line(1, 0, err_file, err_line, "%s: %s", err_func, gai_strerror(err_gai_errno)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment