2 *****************************************************************************
4 * File: http_resolve_host.c
6 * Author: Damien S. Stuart
8 * Purpose: Routine for using an http request to obtain a client's IP
9 * address as seen from the outside world.
11 * Copyright 2009-2010 Damien Stuart (dstuart@dstuart.org)
13 * License (GNU Public License):
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 *****************************************************************************
32 #include "fwknop_common.h"
42 #include <sys/socket.h>
49 char port[MAX_PORT_STR_LEN];
50 char host[MAX_URL_HOST_LEN+1];
51 char path[MAX_URL_PATH_LEN+1];
55 try_url(struct url *url, fko_cli_options_t *options)
57 int sock, res, error, http_buf_len, i;
58 int bytes_read = 0, position = 0;
60 struct addrinfo *result, *rp, hints;
61 char http_buf[HTTP_MAX_REQUEST_LEN];
62 char http_response[HTTP_MAX_RESPONSE_LEN] = {0};
68 /* Winsock needs to be initialized...
70 res = WSAStartup( MAKEWORD(1,1), &wsa_data );
73 fprintf(stderr, "Winsock initialization error %d\n", res );
78 /* Build our HTTP request to resolve the external IP (this is similar to
79 * to contacting whatismyip.org, but using a different URL).
81 snprintf(http_buf, HTTP_MAX_REQUEST_LEN,
82 "GET %s HTTP/1.0\r\nUser-Agent: %s\r\nAccept: */*\r\n"
83 "Host: %s\r\nConnection: close\r\n\r\n",
85 options->http_user_agent,
89 http_buf_len = strlen(http_buf);
91 memset(&hints, 0, sizeof(struct addrinfo));
93 hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
94 hints.ai_socktype = SOCK_STREAM;
95 hints.ai_protocol = IPPROTO_TCP;
97 error = getaddrinfo(url->host, url->port, &hints, &result);
100 fprintf(stderr, "error in getaddrinfo: %s\n", gai_strerror(error));
104 for (rp = result; rp != NULL; rp = rp->ai_next) {
105 sock = socket(rp->ai_family, rp->ai_socktype,
110 if ((error = (connect(sock, rp->ai_addr, rp->ai_addrlen) != -1)))
121 perror("resolve_ip_http: Could not create socket: ");
125 freeaddrinfo(result);
127 if(options->verbose > 1)
128 printf("\nHTTP request: %s\n", http_buf);
130 res = send(sock, http_buf, http_buf_len, 0);
134 perror("resolve_ip_http: write error: ");
136 else if(res != http_buf_len)
139 "[#] Warning: bytes sent (%i) not spa data length (%i).\n",
146 memset(http_buf, 0x0, sizeof(http_buf));
147 bytes_read = recv(sock, http_buf, sizeof(http_buf), 0);
148 if ( bytes_read > 0 ) {
149 if(position + bytes_read >= HTTP_MAX_RESPONSE_LEN)
151 memcpy(&http_response[position], http_buf, bytes_read);
152 position += bytes_read;
155 while ( bytes_read > 0 );
157 http_response[HTTP_MAX_RESPONSE_LEN-1] = '\0';
165 if(options->verbose > 1)
166 printf("\nHTTP response: %s\n", http_response);
168 /* Move to the end of the HTTP header and to the start of the content.
170 ndx = strstr(http_response, "\r\n\r\n");
173 fprintf(stderr, "Did not find the end of HTTP header.\n");
178 /* Walk along the content to try to find the end of the IP address.
179 * Note: We are expecting the content to be just an IP address
180 * (possibly followed by whitespace or other not-digit value).
182 for(i=0; i<MAX_IPV4_STR_LEN; i++) {
183 if(! isdigit(*(ndx+i)) && *(ndx+i) != '.')
187 /* Terminate at the first non-digit and non-dot.
191 /* Now that we have what we think is an IP address string. We make
192 * sure the format and values are sane.
194 if((sscanf(ndx, "%u.%u.%u.%u", &o1, &o2, &o3, &o4)) == 4
195 && o1 >= 0 && o1 <= 255
196 && o2 >= 0 && o2 <= 255
197 && o3 >= 0 && o3 <= 255
198 && o4 >= 0 && o4 <= 255)
200 strlcpy(options->allow_ip_str, ndx, MAX_IPV4_STR_LEN);
203 printf("\n[+] Resolved external IP (via http://%s%s) as: %s\n",
206 options->allow_ip_str);
212 fprintf(stderr, "Invalid IP (%s) in HTTP response:\n\n%s\n",
219 parse_url(char *res_url, struct url* url)
222 int tlen, tlen_offset, port;
224 /* https is not supported.
226 if(strncasecmp(res_url, "https", 5) == 0)
228 fprintf(stderr, "https is not yet supported for http-resolve-ip.\n");
232 /* Strip off http:// portion if necessary
234 if(strncasecmp(res_url, "http://", 7) == 0)
239 /* Look for a colon in case an alternate port was specified.
241 e_ndx = strchr(s_ndx, ':');
244 port = atoi(e_ndx+1);
245 if(port < 1 || port > MAX_PORT)
247 fprintf(stderr, "resolve-url port value is invalid.\n");
251 sprintf(url->port, "%u", port);
253 /* Get the offset we need to skip the port portion when we
254 * extract the hostname part.
256 tlen_offset = strlen(url->port)+1;
260 strlcpy(url->port, "80", 3);
264 /* Get rid of any trailing slash
266 if(res_url[strlen(res_url)-1] == '/')
267 res_url[strlen(res_url)-1] = '\0';
269 e_ndx = strchr(s_ndx, '/');
271 tlen = strlen(s_ndx)+1;
273 tlen = (e_ndx-s_ndx)+1;
277 if(tlen > MAX_URL_HOST_LEN)
279 fprintf(stderr, "resolve-url hostname portion is too large.\n");
282 strlcpy(url->host, s_ndx, tlen);
286 if(strlen(e_ndx) > MAX_URL_PATH_LEN)
288 fprintf(stderr, "resolve-url path portion is too large.\n");
292 strlcpy(url->path, e_ndx, MAX_URL_PATH_LEN);
296 /* default to "GET /" if there isn't a more specific URL
298 strlcpy(url->path, "/", MAX_URL_PATH_LEN);
305 resolve_ip_http(fko_cli_options_t *options)
310 if(options->resolve_url != NULL)
312 if(parse_url(options->resolve_url, &url) < 0)
314 fprintf(stderr, "Error parsing resolve-url\n");
318 res = try_url(&url, options);
321 strlcpy(url.port, "80", 3);
322 strlcpy(url.host, HTTP_RESOLVE_HOST, MAX_URL_HOST_LEN);
323 strlcpy(url.path, HTTP_RESOLVE_URL, MAX_URL_PATH_LEN);
325 res = try_url(&url, options);
328 /* try the backup url (just switches the host to cipherdyne.com)
330 strlcpy(url.host, HTTP_BACKUP_RESOLVE_HOST, MAX_URL_HOST_LEN);
333 res = try_url(&url, options);