From 3029e012389c4ed07fda584b0c4a892ffe32d3e0 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Mon, 27 Jul 2009 05:33:21 +0000 Subject: [PATCH] added the --resolve-ip-http and --user-agent command line args so the fwknop-c client can resolve the external network via http://www.cipherdyne.org/cgi/myip.cgi git-svn-id: file:///home/mbr/svn/fwknop/trunk@121 510a4753-2344-4c79-9c09-4d669213fbeb --- src/config_init.c | 46 ++++++++++++++---- src/config_init.h | 4 +- src/fwknop.c | 115 ++++++++++++++++++++++++++++++++++++++++++++ src/fwknop_common.h | 21 ++++++-- src/spa_comm.h | 4 -- 5 files changed, 173 insertions(+), 17 deletions(-) diff --git a/src/config_init.c b/src/config_init.c index 92a2167c..8ce3cc67 100644 --- a/src/config_init.c +++ b/src/config_init.c @@ -26,7 +26,6 @@ #include "fwknop_common.h" #include "config_init.h" #include "getopt.h" -#include "spa_comm.h" #include "utils.h" #include "ctype.h" @@ -202,16 +201,30 @@ static void validate_options(fko_cli_options_t *options) { /* Gotta have a Destination unless we are just testing or getting the - * the version. + * the version, and must use one of [-s|-R|-a]. */ - if (!options->test && !options->version && !options->show_last_command - && options->spa_server_str[0] == 0x0) + if(!options->test && !options->version && !options->show_last_command) { - fprintf(stderr, - "[*] Must use --destination unless --test mode is used\n"); - exit(EXIT_FAILURE); + if (options->spa_server_str[0] == 0x0) + { + fprintf(stderr, + "[*] Must use --destination unless --test mode is used\n"); + exit(EXIT_FAILURE); + } + if (!options->resolve_ip_http && options->allow_ip_str[0] == 0x0) + { + fprintf(stderr, + "[*] Must use one of [-s|-R|-a] to specify IP for SPA access.\n"); + exit(EXIT_FAILURE); + } + } + if(options->resolve_ip_http || options->spa_proto == FKO_PROTO_HTTP) + if (options->http_user_agent[0] == '\0') + snprintf(options->http_user_agent, HTTP_MAX_USER_AGENT_LEN, + "%s%s", "Fwknop/", MY_VERSION); + /* If we are using gpg, we must at least have the recipient set. */ if(options->use_gpg) @@ -250,7 +263,7 @@ config_init(fko_cli_options_t *options, int argc, char **argv) options->fw_timeout = -1; while ((cmd_arg = getopt_long(argc, argv, - "a:A:bB:C:D:f:gG:hIm:nN:p:P:qQ:rsS:TU:vV", cmd_opts, &index)) != -1) { + "a:A:bB:C:D:f:gG:hIm:nN:p:P:qQ:rRsS:Tu:U:vV", cmd_opts, &index)) != -1) { switch(cmd_arg) { case 'a': @@ -340,6 +353,9 @@ config_init(fko_cli_options_t *options, int argc, char **argv) case 'r': options->rand_port = 1; break; + case 'R': + options->resolve_ip_http = 1; + break; case SHOW_LAST_ARGS: options->show_last_command = 1; break; @@ -356,6 +372,9 @@ config_init(fko_cli_options_t *options, int argc, char **argv) case 'T': options->test = 1; break; + case 'u': + strlcpy(options->http_user_agent, optarg, HTTP_MAX_USER_AGENT_LEN); + break; case 'U': strlcpy(options->spoof_user, optarg, MAX_USERNAME_LEN); break; @@ -447,12 +466,21 @@ usage(void) " the outgoing SPA packet. Note: The 'tcpraw'\n" " and 'icmp' modes use raw sockets and thus\n" " require root access to run.\n" - " -S, --source-port - Set the source port for outgoing SPA packet.\n" " -s, --source-ip - Tell the fwknopd server to accept whatever\n" " source IP the SPA packet has as the IP that\n" " needs access (not recommended, and the\n" " fwknopd server can ignore such requests).\n" + " -S, --source-port - Set the source port for outgoing SPA packet.\n" " -Q, --spoof-source - Set the source IP for outgoing SPA packet.\n" + " -R, --resolve-ip-http - Resolve the external network IP by\n" + " connecting to the URL:\n" + " http://" + HTTP_RESOLVE_HOST + HTTP_RESOLVE_URL + "\n" + " -u, --user-agent - Set the HTTP User-Agent for resolving the\n" + " external IP via -R, or for sending SPA\n" + " packets over HTTP.\n" " -U, --spoof-user - Set the username within outgoing SPA packet.\n" " -q, --quiet - Perform fwknop functions quietly.\n" " -G, --get-key - Load an encryption key/password from a file.\n" diff --git a/src/config_init.h b/src/config_init.h index 982fdc06..3dd74b2f 100644 --- a/src/config_init.h +++ b/src/config_init.h @@ -77,14 +77,16 @@ static struct option cmd_opts[] = {"server-port", 1, NULL, 'p'}, {"server-proto", 1, NULL, 'P'}, {"quiet", 0, NULL, 'q'}, - {"rand-port", 0, NULL, 'r'}, {"spoof-src", 1, NULL, 'Q'}, + {"rand-port", 0, NULL, 'r'}, + {"resolve-ip-http", 0, NULL, 'R'}, {"show-last", 0, NULL, SHOW_LAST_ARGS}, {"source-ip", 0, NULL, 's'}, {"source-port", 1, NULL, 'S'}, {"test", 0, NULL, 'T'}, {"time-offset-plus", 1, NULL, TIME_OFFSET_PLUS}, {"time-offset-minus", 1, NULL, TIME_OFFSET_MINUS}, + {"user-agent", 1, NULL, 'u'}, {"spoof-user", 1, NULL, 'U'}, {"verbose", 0, NULL, 'v'}, {"version", 0, NULL, 'V'}, diff --git a/src/fwknop.c b/src/fwknop.c index 80e51274..571c5688 100644 --- a/src/fwknop.c +++ b/src/fwknop.c @@ -40,6 +40,7 @@ static int set_message_type(fko_ctx_t ctx, fko_cli_options_t *options); static int set_nat_access(fko_ctx_t ctx, fko_cli_options_t *options); static int get_rand_port(fko_ctx_t ctx); static void dump_transmit_options(fko_cli_options_t *options); +static void resolve_ip_http(fko_cli_options_t *options); int main(int argc, char **argv) @@ -113,6 +114,9 @@ main(int argc, char **argv) } else { + if (options.resolve_ip_http) + resolve_ip_http(&options); + /* Set a message string by combining the allow IP and the * port/protocol. The fwknopd server allows no port/protocol * to be specified as well, so in this case append the string @@ -443,6 +447,117 @@ ipv4_str_has_port(char *str) return rv; } +static void resolve_ip_http(fko_cli_options_t *options) +{ + int sock, res, error, http_buf_len, i; + struct addrinfo *result, *rp, hints; + char http_buf[HTTP_MAX_REQUEST_LEN]; + char http_response[HTTP_MAX_RESPONSE_LEN]; + char ip_str[MAX_IP_STR_LEN]; + + /* Build our HTTP request to resolve the external IP (this is similar to + * to contacting whatismyip.org, but using a different URL). + */ + snprintf(http_buf, HTTP_MAX_REQUEST_LEN, + "%s%s%s%s%s%s%s", + "GET ", + HTTP_RESOLVE_URL, + " HTTP/1.0\r\nUser-Agent: ", + options->http_user_agent, + "\r\nAccept: */*\r\nHost: ", + HTTP_RESOLVE_HOST, + "\r\nConnection: Keep-Alive\r\n\r\n" + ); + http_buf_len = strlen(http_buf); + + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + error = getaddrinfo(HTTP_RESOLVE_HOST, "80", &hints, &result); + if (error != 0) + { + fprintf(stderr, "[*] error in getaddrinfo: %s\n", gai_strerror(error)); + exit(EXIT_FAILURE); + } + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sock = socket(rp->ai_family, rp->ai_socktype, + rp->ai_protocol); + if (sock < 0) + continue; + + if (error = connect(sock, rp->ai_addr, rp->ai_addrlen) != -1) + break; /* made it */ + +#ifdef WIN32 + closesocket(sock); +#else + close(sock); +#endif + } + + if (rp == NULL) { + perror("[*] resolve_ip_http: Could not create socket: "); + exit(EXIT_FAILURE); + } + + freeaddrinfo(result); + + res = send(sock, http_buf, http_buf_len, 0); + + if(res < 0) + { + perror("[*] resolve_ip_http: write error: "); + } + else if(res != http_buf_len) + { + fprintf(stderr, + "[#] Warning: bytes sent (%i) not spa data length (%i).\n", + res, http_buf_len + ); + } + + res = read(sock, http_response, HTTP_MAX_RESPONSE_LEN); + http_response[HTTP_MAX_RESPONSE_LEN-1] = '\0'; + +#ifdef WIN32 + closesocket(sock); +#else + close(sock); +#endif + + /* Now parse the response for the IP address (which should be at + * the end of the string + */ + for (i=res-3; i >= 0; i--) + { + if(http_response[i] == '\n') + break; + if(http_response[i] != '.' && ! isdigit(http_response[i])) + { + fprintf(stderr, "[*] Invalid IP in HTTP response.\n"); + exit(EXIT_FAILURE); + } + } + if (i < MIN_IP_STR_LEN) + { + fprintf(stderr, "[*] Invalid IP in HTTP response.\n"); + exit(EXIT_FAILURE); + } + http_response[res-1] = '\0'; + + strlcpy(options->allow_ip_str, + (http_response + i+1), (res - (i+2))); + + printf("[+] Resolved external IP (via http://%s%s) as: %s\n", + HTTP_RESOLVE_HOST, HTTP_RESOLVE_URL, options->allow_ip_str); + + return; +} + /* Set NAT access string */ static int diff --git a/src/fwknop_common.h b/src/fwknop_common.h index 730cf898..3efe4bcf 100644 --- a/src/fwknop_common.h +++ b/src/fwknop_common.h @@ -97,6 +97,7 @@ enum { #define MAX_PORT_STR_LEN 6 #define MAX_PROTO_STR_LEN 6 #define MAX_IP_STR_LEN 16 +#define MIN_IP_STR_LEN 9 #define MAX_SERVER_STR_LEN 50 #define MAX_LINE_LEN 1024 @@ -104,6 +105,17 @@ enum { #define MAX_GPG_KEY_ID 128 #define MAX_USERNAME_LEN 30 +/* For resolving the allow IP via HTTP and sending SPA packets over + * HTTP +*/ +#define HTTP_RESOLVE_HOST "www.cipherdyne.org" +#define HTTP_RESOLVE_URL "/cgi/myip.cgi" +#define HTTP_MAX_REQUEST_LEN 2000 +#define HTTP_MAX_RESPONSE_LEN 2000 +#define HTTP_MAX_USER_AGENT_LEN 50 + +/* For time offset handling +*/ #define MAX_TIME_STR_LEN 9 enum { TIME_OFFSET_SECONDS, @@ -112,9 +124,7 @@ enum { TIME_OFFSET_DAYS }; -#define RAND_FILE "/dev/urandom" - -/* fwkop client configuration parameters and values +/* fwknop client configuration parameters and values */ typedef struct fko_cli_options { @@ -142,6 +152,11 @@ typedef struct fko_cli_options int nat_port; int nat_rand_port; + /* External IP resolution via HTTP + */ + int resolve_ip_http; + char http_user_agent[HTTP_MAX_USER_AGENT_LEN]; + /* SPA packet transmission port and protocol */ int spa_proto; diff --git a/src/spa_comm.h b/src/spa_comm.h index 4cee31e3..66f4f952 100644 --- a/src/spa_comm.h +++ b/src/spa_comm.h @@ -124,10 +124,6 @@ struct icmphdr } un; }; -/* for sending SPA packets over HTTP -*/ -#define HTTP_MAX_REQUEST_LEN 2000 /* bytes - reasonable maximum */ - #define ICMP_ECHOREPLY 0 /* Echo Reply */ #define ICMP_DEST_UNREACH 3 /* Destination Unreachable */ #define ICMP_SOURCE_QUENCH 4 /* Source Quench */