Merge pull request #34 from Kait0/DNSRes

Added DNS Resolution feature to the client.
This commit is contained in:
LiteSpeed Tech 2018-05-16 12:26:09 -04:00 committed by GitHub
commit f6b053a9b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 117 additions and 44 deletions

View file

@ -5,7 +5,8 @@ to the LiteSpeed Client Library:
- Brian Prodoehl -- Docker file - Brian Prodoehl -- Docker file
- Amol Desphande -- Windows support - Amol Desphande -- Windows support
- Alexis La Goutte -- Travis-CI integration - Alexis La Goutte -- Travis-CI integration
- Bernhard Jaeger -- DNS Resolution
Thank you! Thank you!
We welcome contributions in any form -- patches, issues, pull requests. We welcome contributions in any form -- patches, issues, pull requests.

View file

@ -9,24 +9,25 @@ Usage Examples
Fetch Google's home page: Fetch Google's home page:
./http_client -H www.google.com -s 74.125.22.106:443 -p / ./http_client -H www.google.com -s 443 -p /
or even
./http_client -H www.google.co.uk -s 2a00:1450:4009:80c::2003:443 -p /
In the example above, -H specifies the domain; it is also used as the value In the example above, -H specifies the domain; it is also used as the value
of SNI paramater in the handshake. of SNI paramater in the handshake.
The IP address and the port number are specified using the -s flag. The port number is specified using the -s flag.
The ip adress is determined automatically.
The -6 flag tells the program to use IPv6 instead of IPv4.
Note that -6 must be used before -s in order to work.
(Since www.google.com has different IP addresses in different regions, check You can also specify a certain ip-address with the -s command.
that you are using the correct IP address by resolving www.google.com first Note that this example won't work everywhere since google has a lot of different ips.
by using nslookup, dig, ping, or some other tool.) You have to look up the correct one for yourself.
./http_client -H www.google.com -s 172.217.22.4:443 -p /
POST a file to calculate its CRC32 checksum: POST a file to calculate its CRC32 checksum:
./http_client -H www.litespeedtech.com -s 127.0.0.1:443 \ ./http_client -H www.litespeedtech.com -s 443 \
-p /cgi-bin/crc32.cgi -P file-256M -M POST -p /cgi-bin/crc32.cgi -P file-256M -M POST
HTTP/1.1 200 OK HTTP/1.1 200 OK
@ -50,7 +51,7 @@ On the command line, I do
To submit several requests concurrently, one can use -n and -r options: To submit several requests concurrently, one can use -n and -r options:
./http_client -H www.litespeedtech.com -s 127.0.0.1:443 \ ./http_client -H www.litespeedtech.com -s 443 \
-p /cgi-bin/crc32.cgi -P file-256M -M POST -n 3 -r 10 -p /cgi-bin/crc32.cgi -P file-256M -M POST -n 3 -r 10
This will open three parallel connections which will make ten POST This will open three parallel connections which will make ten POST

View file

@ -2,7 +2,6 @@
/* /*
* http_client.c -- A simple HTTP/QUIC client * http_client.c -- A simple HTTP/QUIC client
*/ */
#ifndef WIN32 #ifndef WIN32
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netinet/in.h> #include <netinet/in.h>
@ -159,7 +158,6 @@ struct lsquic_stream_ctx {
struct lsquic_reader reader; struct lsquic_reader reader;
}; };
static lsquic_stream_ctx_t * static lsquic_stream_ctx_t *
http_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream) http_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
{ {
@ -313,7 +311,7 @@ http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
unsigned char buf[0x200]; unsigned char buf[0x200];
unsigned nreads = 0; unsigned nreads = 0;
#ifdef WIN32 #ifdef WIN32
srand(GetTickCount()); srand(GetTickCount());
#endif #endif
do do
@ -409,6 +407,7 @@ usage (const char *prog)
"Usage: %s [opts]\n" "Usage: %s [opts]\n"
"\n" "\n"
"Options:\n" "Options:\n"
" -6 MUST be entered before -s in order to work."
" -p PATH Path to request. May be specified more than once.\n" " -p PATH Path to request. May be specified more than once.\n"
" -n CONNS Number of concurrent connections. Defaults to 1.\n" " -n CONNS Number of concurrent connections. Defaults to 1.\n"
" -r NREQS Total number of requests to send. Defaults to 1.\n" " -r NREQS Total number of requests to send. Defaults to 1.\n"
@ -421,6 +420,8 @@ usage (const char *prog)
" content-length\n" " content-length\n"
" -K Discard server response\n" " -K Discard server response\n"
" -I Abort on incomplete reponse from server\n" " -I Abort on incomplete reponse from server\n"
" -6 IPv6 The client will try to connect via IPv6\n"
" if this flag is used. If not it will use IPv4.\n"
, prog); , prog);
} }
@ -435,6 +436,8 @@ main (int argc, char **argv)
struct sport_head sports; struct sport_head sports;
struct prog prog; struct prog prog;
ipv6 = 0;
TAILQ_INIT(&sports); TAILQ_INIT(&sports);
memset(&client_ctx, 0, sizeof(client_ctx)); memset(&client_ctx, 0, sizeof(client_ctx));
client_ctx.hcc_concurrency = 1; client_ctx.hcc_concurrency = 1;
@ -453,9 +456,12 @@ main (int argc, char **argv)
prog_init(&prog, LSENG_HTTP, &sports, &http_client_if, &client_ctx); prog_init(&prog, LSENG_HTTP, &sports, &http_client_if, &client_ctx);
while (-1 != (opt = getopt(argc, argv, PROG_OPTS "r:R:IKu:EP:M:n:H:p:h"))) while (-1 != (opt = getopt(argc, argv, PROG_OPTS "6r:R:IKu:EP:M:n:H:p:h")))
{ {
switch (opt) { switch (opt) {
case '6':
ipv6 = 1;
break;
case 'I': case 'I':
client_ctx.hcc_flags |= HCC_ABORT_ON_INCOMPLETE; client_ctx.hcc_flags |= HCC_ABORT_ON_INCOMPLETE;
break; break;
@ -499,7 +505,7 @@ main (int argc, char **argv)
break; break;
case 'H': case 'H':
client_ctx.hostname = optarg; client_ctx.hostname = optarg;
prog.prog_hostname = optarg; /* Pokes into prog */ prog.prog_hostname = optarg; /* Pokes into prog */
break; break;
case 'p': case 'p':
pe = calloc(1, sizeof(*pe)); pe = calloc(1, sizeof(*pe));

View file

@ -1,6 +1,7 @@
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */ /* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h> #include <assert.h>
#ifndef WIN32 #ifndef WIN32
#include <arpa/inet.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <signal.h> #include <signal.h>
#endif #endif
@ -82,12 +83,12 @@ void
prog_print_common_options (const struct prog *prog, FILE *out) prog_print_common_options (const struct prog *prog, FILE *out)
{ {
fprintf(out, fprintf(out,
" -s SVCPORT Service port. Takes on the form of IPv4:port or\n" " -s SVCPORT The port on which the client should connect.\n"
" IPv6:port. For example:\n" " If no -s option is given, port 443\n"
" 127.0.0.1:12345\n"
" ::1:12345\n"
" If no -s option is given, 0.0.0.0:12345 address\n"
" is used.\n" " is used.\n"
" You can also specify an certain ipadress to be used here.\n"
" To do that enter an ipadress and the port seperated by :\n"
" e.g -s ::1:12345 or -s 0.0.0.0:12345 or -s 443 or -s example.com:443\n"
#if LSQUIC_DONTFRAG_SUPPORTED #if LSQUIC_DONTFRAG_SUPPORTED
" -D Set `do not fragment' flag on outgoing UDP packets\n" " -D Set `do not fragment' flag on outgoing UDP packets\n"
#endif #endif
@ -175,6 +176,7 @@ prog_set_opt (struct prog *prog, int opt, const char *arg)
case 'o': case 'o':
return set_engine_option(&prog->prog_settings, return set_engine_option(&prog->prog_settings,
&prog->prog_version_cleared, arg); &prog->prog_version_cleared, arg);
case 's': case 's':
if (0 == (prog->prog_engine_flags & LSENG_SERVER) && if (0 == (prog->prog_engine_flags & LSENG_SERVER) &&
!TAILQ_EMPTY(prog->prog_sports)) !TAILQ_EMPTY(prog->prog_sports))
@ -371,7 +373,7 @@ prog_prep (struct prog *prog)
if (TAILQ_EMPTY(prog->prog_sports)) if (TAILQ_EMPTY(prog->prog_sports))
{ {
s = prog_add_sport(prog, "0.0.0.0:12345"); s = prog_add_sport(prog, "443");
if (0 != s) if (0 != s)
return -1; return -1;
} }

View file

@ -6,6 +6,8 @@
#ifndef PROG_H #ifndef PROG_H
#define PROG_H 1 #define PROG_H 1
int ipv6; /*True = Program uses ipv6, False = Program uses ipv4*/
struct event; struct event;
struct event_base; struct event_base;
struct lsquic_hash; struct lsquic_hash;

View file

@ -146,6 +146,45 @@ static void getExtensionPtrs()
#endif #endif
int get_Ip_from_DNS(const char* hostname, struct service_port * sport, const char* port, int version)
{
struct sockaddr_in *const sa4 = (void *)&sport->sas;
struct sockaddr_in6 *const sa6 = (void *)&sport->sas;
struct addrinfo hints, *servinfo;
int rv;
char ip[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof(hints));
if (version)
hints.ai_family = AF_INET6;
else
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(hostname, port, &hints, &servinfo)) != 0){
LSQ_ERROR("getaddrinfo: %s\n", gai_strerror(rv));
freeaddrinfo(servinfo);
return 1;
}
if (version){ /*Ipv6*/
memset(sa6, 0, sizeof(*sa6));
sa6->sin6_addr = ((struct sockaddr_in6*) servinfo->ai_addr)->sin6_addr;
sa6->sin6_family = AF_INET6;
sa6->sin6_port = htons(atoi(port));
inet_ntop(AF_INET6, &sa6->sin6_addr, ip, INET6_ADDRSTRLEN);
}
else{/*Ipv4*/
sa4->sin_addr = ((struct sockaddr_in *) servinfo->ai_addr)->sin_addr;
sa4->sin_family = AF_INET;
sa4->sin_port = htons(atoi(port));
inet_ntop(AF_INET, &sa4->sin_addr, ip, INET6_ADDRSTRLEN);
}
memcpy(sport->host, ip, sizeof(ip));
freeaddrinfo(servinfo);
return 0;
}
static struct packets_in * static struct packets_in *
allocate_packets_in (SOCKET_TYPE fd) allocate_packets_in (SOCKET_TYPE fd)
@ -225,38 +264,51 @@ sport_new (const char *optarg, struct prog *prog)
if (if_name) if (if_name)
{ {
strncpy(sport->if_name, if_name + 1, sizeof(sport->if_name) - 1); strncpy(sport->if_name, if_name + 1, sizeof(sport->if_name) - 1);
sport->if_name[ sizeof(sport->if_name) - 1 ] = '\0'; sport->if_name[sizeof(sport->if_name) - 1] = '\0';
*if_name = '\0'; *if_name = '\0';
} }
else else
sport->if_name[0] = '\0'; sport->if_name[0] = '\0';
#endif #endif
char *port = strrchr(addr, ':'); char *port = strrchr(addr, ':');
if (!port) if (!port){/*IpAdress wasn't specified by the user*/
goto err; if (get_Ip_from_DNS(prog->prog_hostname, sport, addr, ipv6) == 1)
*port = '\0'; goto err;
++port; }
if ((uintptr_t) port - (uintptr_t) addr > sizeof(sport->host)) else {
goto err; *port = '\0';
memcpy(sport->host, addr, port - addr); ++port;
struct sockaddr_in *const sa4 = (void *)&sport->sas;
struct sockaddr_in6 *const sa6 = (void *)&sport->sas;
if (inet_pton(AF_INET, addr, &sa4->sin_addr)){
sa4->sin_family = AF_INET;
sa4->sin_port = htons(atoi(port));
struct sockaddr_in *const sa4 = (void *) &sport->sas; if ((uintptr_t)port - (uintptr_t)addr > sizeof(sport->host))
struct sockaddr_in6 *const sa6 = (void *) &sport->sas; goto err;
if (inet_pton(AF_INET, addr, &sa4->sin_addr)) { memcpy(sport->host, addr, port - addr);
sa4->sin_family = AF_INET; }
sa4->sin_port = htons(atoi(port)); else if (memset(sa6, 0, sizeof(*sa6)), inet_pton(AF_INET6, addr, &sa6->sin6_addr)){
} else if (memset(sa6, 0, sizeof(*sa6)), sa6->sin6_family = AF_INET6;
inet_pton(AF_INET6, addr, &sa6->sin6_addr)) { sa6->sin6_port = htons(atoi(port));
sa6->sin6_family = AF_INET6;
sa6->sin6_port = htons(atoi(port));
} else
goto err;
if ((uintptr_t)port - (uintptr_t)addr > sizeof(sport->host))
goto err;
memcpy(sport->host, addr, port - addr);
}
else if (get_Ip_from_DNS(addr, sport, port, ipv6) != 0)
goto err;
}
free(addr); free(addr);
sport->sp_prog = prog; sport->sp_prog = prog;
return sport; return sport;
err: err:
LSQ_ERROR("Couldn't resolve hostname or ip-address");
free(sport); free(sport);
free(addr); free(addr);
return NULL; return NULL;
@ -404,7 +456,7 @@ read_one_packet (struct read_iter *iter)
if (SOCKET_ERROR == socket_ret) { if (SOCKET_ERROR == socket_ret) {
if (WSAEWOULDBLOCK != WSAGetLastError()) if (WSAEWOULDBLOCK != WSAGetLastError())
LSQ_ERROR("recvmsg: %d", WSAGetLastError()); LSQ_ERROR("recvmsg: %d", WSAGetLastError());
return ROP_ERROR; return ROP_ERROR;
} }
#endif #endif

View file

@ -106,4 +106,13 @@ create_lsquic_reader_ctx (const char *filename);
void void
destroy_lsquic_reader_ctx (struct reader_ctx *ctx); destroy_lsquic_reader_ctx (struct reader_ctx *ctx);
/*Function resolves a Hostname into an Ip Adress
Parameters:
-hostname the URL of the website
-sport the service port structure that stores the ip
-port the port of the connection
-version 0 for ipv4 and 1 for ipv6
*/
int
get_Ip_from_DNS(const char* hostname, struct service_port * sport, const char* port, int version);
#endif #endif