2009-08-16 12:30:26 +00:00
/*
2014-06-01 06:46:42 +00:00
* Copyright ( c ) 2006 - 2014 Erik Ekman < yarrick @ kryo . se > ,
2015-08-21 08:57:54 +00:00
* 2006 - 2009 Bjorn Andersson < flex @ kryo . se > ,
* 2015 Frekk van Blagh < frekk @ frekkworks . com >
2009-08-16 12:30:26 +00:00
*
2014-08-07 19:14:10 +00:00
* Permission to use , copy , modify , and / or distribute this software for any
2009-08-16 12:30:26 +00:00
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
*
* THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
* ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*/
2009-09-20 15:11:14 +00:00
# include <ctype.h>
2009-08-16 12:30:26 +00:00
# include <stdio.h>
# include <stdint.h>
# include <stdlib.h>
# include <string.h>
2014-05-29 16:37:40 +00:00
# include <strings.h>
2009-08-16 12:30:26 +00:00
# include <signal.h>
# include <unistd.h>
# include <sys/param.h>
# include <fcntl.h>
# include <zlib.h>
2015-09-28 05:07:00 +00:00
# include <sys/time.h>
2009-08-16 12:30:26 +00:00
# include <time.h>
# ifdef WINDOWS32
# include "windows.h"
# else
2015-08-05 17:33:50 +00:00
# include <arpa/nameser.h>
2011-04-23 18:55:59 +00:00
# ifdef ANDROID
# include "android_dns.h"
# endif
2009-08-16 12:30:26 +00:00
# ifdef DARWIN
2009-09-19 08:32:57 +00:00
# define BIND_8_COMPAT
# include <arpa/nameser_compat.h>
2009-08-16 12:30:26 +00:00
# endif
# include <grp.h>
# include <pwd.h>
# include <netdb.h>
# endif
# include "common.h"
# include "encoding.h"
# include "base32.h"
# include "base64.h"
2009-12-29 20:00:57 +00:00
# include "base64u.h"
# include "base128.h"
2009-08-16 12:30:26 +00:00
# include "dns.h"
# include "login.h"
# include "tun.h"
# include "version.h"
2015-08-21 03:08:47 +00:00
# include "window.h"
2015-10-03 14:09:34 +00:00
# include "util.h"
2009-08-16 12:30:26 +00:00
# include "client.h"
2015-10-04 14:13:47 +00:00
/* Output flags for debug and time between stats update */
2015-08-29 12:11:21 +00:00
int debug ;
2015-10-04 14:13:47 +00:00
int stats ;
2015-08-29 12:11:21 +00:00
2009-08-16 12:30:26 +00:00
static int running ;
static const char * password ;
2015-08-14 14:07:48 +00:00
/* Nameserver/domain info */
static struct sockaddr_storage * nameserv_addrs ;
static int nameserv_addrs_len ;
static int current_nameserver ;
2015-06-28 20:41:54 +00:00
static struct sockaddr_storage raw_serv ;
static int raw_serv_len ;
2009-08-16 12:30:26 +00:00
static const char * topdomain ;
static uint16_t rand_seed ;
2015-08-21 03:08:47 +00:00
/* Current up/downstream window data */
static struct frag_buffer * outbuf ;
static struct frag_buffer * inbuf ;
2015-09-28 05:07:00 +00:00
static size_t windowsize_up ;
static size_t windowsize_down ;
static size_t maxfragsize_up ;
2015-08-21 03:08:47 +00:00
/* Next downstream seqID to be ACK'd (-1 if none pending) */
static int next_downstream_ack ;
2009-08-16 12:30:26 +00:00
2015-09-28 05:07:00 +00:00
/* Remembering queries we sent for tracking purposes */
static struct query_tuple * pending_queries ;
2015-10-03 14:09:34 +00:00
static size_t num_pending ;
2015-09-28 05:07:00 +00:00
static time_t max_timeout_ms ;
/* Server response timeout in ms */
static time_t server_timeout_ms ;
static int autodetect_server_timeout ;
/* Cumulative Round-Trip-Time in ms */
static time_t rtt_total_ms ;
static size_t num_immediate ;
2015-10-04 14:13:47 +00:00
/* Connection statistics */
2015-09-28 05:07:00 +00:00
static size_t num_timeouts ;
static size_t num_untracked ;
2015-10-04 14:13:47 +00:00
static size_t num_servfail ;
static size_t num_badip ;
static size_t num_init_queries ;
static size_t num_sent ;
static size_t num_recv ;
static size_t send_query_sendcnt = 0 ;
static size_t send_query_recvcnt = 0 ;
static size_t num_frags_sent ;
static size_t num_frags_recv ;
static size_t num_pings ;
2015-09-28 05:07:00 +00:00
2009-08-16 12:30:26 +00:00
/* My userid at the server */
static char userid ;
2009-12-29 20:00:57 +00:00
static char userid_char ; /* used when sending (lowercase) */
static char userid_char2 ; /* also accepted when receiving (uppercase) */
2009-08-16 12:30:26 +00:00
static uint16_t chunkid ;
2009-09-20 15:11:14 +00:00
/* Base32 encoder used for non-data packets and replies */
2009-08-16 12:30:26 +00:00
static struct encoder * b32 ;
2009-12-29 20:00:57 +00:00
/* Base64 etc encoders for replies */
2009-09-20 15:11:14 +00:00
static struct encoder * b64 ;
2009-12-29 20:00:57 +00:00
static struct encoder * b64u ;
static struct encoder * b128 ;
2009-08-16 12:30:26 +00:00
/* The encoder used for data packets
* Defaults to Base32 , can be changed after handshake */
static struct encoder * dataenc ;
2014-06-01 06:34:18 +00:00
2015-09-28 05:07:00 +00:00
/* Upstream/downstream compression flags */
static int compression_up ;
static int compression_down ;
2009-09-20 15:11:14 +00:00
/* The encoder to use for downstream data */
static char downenc = ' ' ;
2014-06-01 06:34:18 +00:00
2009-09-20 15:11:14 +00:00
/* set query type to send */
2015-09-28 05:07:00 +00:00
static uint16_t do_qtype = T_UNSET ;
2009-08-16 12:30:26 +00:00
/* My connection mode */
static enum connection conn ;
2009-12-29 20:00:57 +00:00
static int lazymode ;
static long send_ping_soon ;
static time_t lastdownstreamtime ;
2015-08-23 14:14:27 +00:00
static size_t hostname_maxlen = 0xFF ;
2009-09-20 21:10:44 +00:00
2009-08-16 12:30:26 +00:00
void
client_init ( )
{
running = 1 ;
b32 = get_base32_encoder ( ) ;
2009-09-20 15:11:14 +00:00
b64 = get_base64_encoder ( ) ;
2009-12-29 20:00:57 +00:00
b64u = get_base64u_encoder ( ) ;
b128 = get_base128_encoder ( ) ;
2009-08-16 12:30:26 +00:00
dataenc = get_base32_encoder ( ) ;
2015-09-28 05:07:00 +00:00
rand_seed = ( uint16_t ) rand ( ) ;
2009-09-20 21:10:44 +00:00
send_ping_soon = 1 ; /* send ping immediately after startup */
2009-08-16 12:30:26 +00:00
conn = CONN_DNS_NULL ;
2009-09-20 21:10:44 +00:00
2015-09-28 05:07:00 +00:00
chunkid = ( uint16_t ) rand ( ) ;
2009-09-20 21:10:44 +00:00
2015-09-28 05:07:00 +00:00
/* RFC says timeout minimum 5sec */
max_timeout_ms = 5000 ;
windowsize_up = 8 ;
windowsize_down = 8 ;
compression_up = 0 ;
compression_down = 1 ;
2015-08-14 14:07:48 +00:00
2015-08-21 03:08:47 +00:00
next_downstream_ack = - 1 ;
2015-08-14 14:07:48 +00:00
current_nameserver = 0 ;
2015-09-28 05:07:00 +00:00
maxfragsize_up = 100 ;
2015-10-04 14:13:47 +00:00
num_immediate = 1 ;
rtt_total_ms = 1000 ;
2015-09-28 05:07:00 +00:00
outbuf = NULL ;
inbuf = NULL ;
pending_queries = NULL ;
2009-08-16 12:30:26 +00:00
}
void
client_stop ( )
{
running = 0 ;
}
enum connection
client_get_conn ( )
{
return conn ;
}
void
2015-08-14 14:07:48 +00:00
client_set_nameservers ( struct sockaddr_storage * addr , int addrslen )
2009-08-16 12:30:26 +00:00
{
2015-08-14 14:07:48 +00:00
nameserv_addrs = addr ;
nameserv_addrs_len = addrslen ;
2009-08-16 12:30:26 +00:00
}
void
client_set_topdomain ( const char * cp )
{
topdomain = cp ;
}
void
client_set_password ( const char * cp )
{
password = cp ;
}
2014-06-09 16:55:27 +00:00
int
2014-06-09 16:11:16 +00:00
client_set_qtype ( char * qtype )
2009-09-20 15:11:14 +00:00
{
if ( ! strcasecmp ( qtype , " NULL " ) )
do_qtype = T_NULL ;
2014-06-09 18:05:29 +00:00
else if ( ! strcasecmp ( qtype , " PRIVATE " ) )
do_qtype = T_PRIVATE ;
2009-09-20 15:11:14 +00:00
else if ( ! strcasecmp ( qtype , " CNAME " ) )
do_qtype = T_CNAME ;
else if ( ! strcasecmp ( qtype , " A " ) )
do_qtype = T_A ;
else if ( ! strcasecmp ( qtype , " MX " ) )
do_qtype = T_MX ;
2009-12-29 20:00:57 +00:00
else if ( ! strcasecmp ( qtype , " SRV " ) )
do_qtype = T_SRV ;
2009-09-20 15:11:14 +00:00
else if ( ! strcasecmp ( qtype , " TXT " ) )
do_qtype = T_TXT ;
2014-06-09 16:55:27 +00:00
return ( do_qtype = = T_UNSET ) ;
2009-09-20 15:11:14 +00:00
}
2009-12-29 20:00:57 +00:00
char *
2014-06-09 16:11:16 +00:00
client_get_qtype ( )
2009-12-29 20:00:57 +00:00
{
char * c = " UNDEFINED " ;
if ( do_qtype = = T_NULL ) c = " NULL " ;
2014-06-09 18:05:29 +00:00
else if ( do_qtype = = T_PRIVATE ) c = " PRIVATE " ;
2009-12-29 20:00:57 +00:00
else if ( do_qtype = = T_CNAME ) c = " CNAME " ;
else if ( do_qtype = = T_A ) c = " A " ;
else if ( do_qtype = = T_MX ) c = " MX " ;
else if ( do_qtype = = T_SRV ) c = " SRV " ;
else if ( do_qtype = = T_TXT ) c = " TXT " ;
return c ;
}
2009-09-20 15:11:14 +00:00
void
2014-06-09 16:11:16 +00:00
client_set_downenc ( char * encoding )
2009-09-20 15:11:14 +00:00
{
if ( ! strcasecmp ( encoding , " base32 " ) )
downenc = ' T ' ;
else if ( ! strcasecmp ( encoding , " base64 " ) )
downenc = ' S ' ;
2009-12-29 20:00:57 +00:00
else if ( ! strcasecmp ( encoding , " base64u " ) )
downenc = ' U ' ;
else if ( ! strcasecmp ( encoding , " base128 " ) )
downenc = ' V ' ;
2009-09-20 15:11:14 +00:00
else if ( ! strcasecmp ( encoding , " raw " ) )
downenc = ' R ' ;
}
2014-06-01 06:34:18 +00:00
void
2015-09-28 05:07:00 +00:00
client_set_compression ( int up , int down )
{
compression_up = up ;
compression_down = down ;
}
void
client_set_dnstimeout ( double timeout , double servertimeout , int autodetect )
2009-09-20 21:10:44 +00:00
{
2015-09-28 05:07:00 +00:00
max_timeout_ms = timeout * 1000 ;
server_timeout_ms = servertimeout * 1000 ;
autodetect_server_timeout = autodetect ;
2009-09-20 21:10:44 +00:00
}
void
2009-09-20 21:10:46 +00:00
client_set_lazymode ( int lazy_mode )
{
lazymode = lazy_mode ;
2009-09-20 21:10:44 +00:00
}
2015-09-28 05:07:00 +00:00
void
client_set_windowsize ( size_t up , size_t down )
/* set window sizes for upstream and downstream
* XXX upstream / downstream windowsizes might as well be the same */
{
windowsize_up = up ;
windowsize_down = down ;
}
2009-12-29 20:00:57 +00:00
void
2015-08-23 14:14:27 +00:00
client_set_hostname_maxlen ( size_t i )
2009-12-29 20:00:57 +00:00
{
2015-08-23 14:14:27 +00:00
if ( i < = 0xFF & & i ! = hostname_maxlen ) {
2009-12-29 20:00:57 +00:00
hostname_maxlen = i ;
2015-09-28 05:07:00 +00:00
maxfragsize_up = get_raw_length_from_dns ( hostname_maxlen - UPSTREAM_HDR , dataenc , topdomain ) ;
if ( outbuf )
outbuf - > maxfraglen = maxfragsize_up ;
2015-08-23 14:14:27 +00:00
}
2009-12-29 20:00:57 +00:00
}
2009-08-16 12:30:26 +00:00
const char *
client_get_raw_addr ( )
{
2015-06-28 20:41:54 +00:00
return format_addr ( & raw_serv , raw_serv_len ) ;
2009-08-16 12:30:26 +00:00
}
2015-08-14 14:07:48 +00:00
void
client_rotate_nameserver ( )
{
current_nameserver + + ;
if ( current_nameserver > = nameserv_addrs_len )
current_nameserver = 0 ;
}
2015-09-28 05:07:00 +00:00
/* Client-side query tracking for lazy mode */
2015-10-03 14:09:34 +00:00
/* Handy macro for printing stats with messages */
# define QTRACK_DEBUG(l, ...) \
if ( debug > = l ) { \
fprintf ( stderr , " [QTRACK (%lu/%lu), ? %lu, TO %lu, S %lu/%lu] " , num_pending , PENDING_QUERIES_LENGTH , \
num_untracked , num_timeouts , window_sending ( outbuf ) , outbuf - > numitems ) ; \
fprintf ( stderr , __VA_ARGS__ ) ; \
fprintf ( stderr , " \n " ) ; \
}
2015-10-04 14:13:47 +00:00
static void
update_server_timeout ( int dns_fd , int handshake )
/* Calculate server timeout based on average RTT, send ping "handshake" to set */
{
time_t rtt_ms ;
static size_t num_rtt_timeouts = 0 ;
/* Get average RTT in ms */
rtt_ms = rtt_total_ms / num_immediate ;
if ( rtt_ms > = max_timeout_ms ) {
num_rtt_timeouts + + ;
if ( num_rtt_timeouts < 3 ) {
fprintf ( stderr , " Target interval of %ld ms less than average round-trip of "
" %ld ms! Try increasing interval with -I. \n " , max_timeout_ms , rtt_ms ) ;
} else {
max_timeout_ms = server_timeout_ms ;
if ( server_timeout_ms > rtt_ms ) {
server_timeout_ms - = rtt_ms ;
if ( lazymode )
fprintf ( stderr , " Adjusting server timeout to %ld ms, target interval %ld ms. \n " ,
server_timeout_ms , max_timeout_ms ) ;
} else {
server_timeout_ms = 0 ;
fprintf ( stderr , " Switching off lazy mode to keep timeouts below target interval (%ld ms). " ,
max_timeout_ms ) ;
handshake_switch_options ( dns_fd , 0 , compression_down , downenc ) ;
}
num_rtt_timeouts = 0 ;
}
} else {
/* Set server timeout based on target interval and RTT */
server_timeout_ms = max_timeout_ms - rtt_ms ;
}
if ( ! handshake )
return ;
/* Send ping handshake to set server timeout/lazymode */
send_ping ( dns_fd , 1 , - 1 ) ;
}
2015-10-03 14:09:34 +00:00
static void
check_pending_queries ( )
/* Updates pending queries list */
2015-09-28 05:07:00 +00:00
{
2015-10-03 14:09:34 +00:00
num_pending = 0 ;
2015-09-28 05:07:00 +00:00
struct timeval now , qtimeout , max_timeout ;
gettimeofday ( & now , NULL ) ;
/* Max timeout for queries is max interval + 1 second extra */
2015-10-04 14:13:47 +00:00
max_timeout = ms_to_timeval ( max_timeout_ms + 1000 ) ;
2015-09-28 05:07:00 +00:00
for ( int i = 0 ; i < PENDING_QUERIES_LENGTH ; i + + ) {
2015-10-03 14:09:34 +00:00
if ( pending_queries [ i ] . time . tv_sec > 0 & & pending_queries [ i ] . id > = 0 ) {
2015-09-28 05:07:00 +00:00
timeradd ( & pending_queries [ i ] . time , & max_timeout , & qtimeout ) ;
2015-10-03 14:09:34 +00:00
if ( ! timercmp ( & qtimeout , & now , > ) ) {
2015-09-28 05:07:00 +00:00
/* Query has timed out, clear it */
pending_queries [ i ] . time . tv_sec = 0 ;
num_timeouts + + ;
}
2015-10-03 14:09:34 +00:00
num_pending + + ;
2015-09-28 05:07:00 +00:00
}
}
}
static void
query_sent_now ( int id )
{
2015-10-03 14:09:34 +00:00
int i = 0 , found = 0 ;
2015-09-28 05:07:00 +00:00
if ( ! pending_queries )
return ;
if ( id < 0 | | id > 65535 )
return ;
2015-10-03 14:09:34 +00:00
/* Replace any empty queries first, then timed out ones if necessary */
for ( i = 0 ; i < PENDING_QUERIES_LENGTH ; i + + ) {
if ( pending_queries [ i ] . id < 0 ) {
found = 1 ;
2015-09-28 05:07:00 +00:00
break ;
}
}
2015-10-03 14:09:34 +00:00
if ( ! found ) {
for ( i = 0 ; i < PENDING_QUERIES_LENGTH ; i + + ) {
if ( pending_queries [ i ] . time . tv_sec = = 0 ) {
found = 1 ;
break ;
}
}
}
/* if no slots found after both checks */
if ( ! found ) {
QTRACK_DEBUG ( 1 , " Buffer full! Failed to add id %d. " , id ) ;
} else {
/* Add query into found location */
pending_queries [ i ] . id = id ;
gettimeofday ( & pending_queries [ i ] . time , NULL ) ;
num_pending + + ;
QTRACK_DEBUG ( 4 , " Adding query id %d into pending_queries[%d] " , id , i ) ;
id = - 1 ;
}
2015-09-28 05:07:00 +00:00
}
2009-08-16 12:30:26 +00:00
static void
2015-10-04 14:13:47 +00:00
got_response ( int id , int immediate , int dns_fd )
2015-09-28 05:07:00 +00:00
{
struct timeval now , rtt ;
gettimeofday ( & now , NULL ) ;
2015-10-03 14:09:34 +00:00
QTRACK_DEBUG ( 4 , " Got answer id %d (%s) " , id , immediate ? " immediate " : " lazy " ) ;
2015-09-28 05:07:00 +00:00
for ( int i = 0 ; i < PENDING_QUERIES_LENGTH ; i + + ) {
if ( id > = 0 & & pending_queries [ i ] . id = = id ) {
2015-10-03 14:09:34 +00:00
if ( num_pending > 0 )
num_pending - - ;
QTRACK_DEBUG ( 5 , " found answer id %d in pending queries " , id ) ;
2015-09-28 05:07:00 +00:00
id = - 1 ;
if ( pending_queries [ i ] . time . tv_sec = = 0 & & num_timeouts > 0 ) {
2015-10-03 14:09:34 +00:00
/* If query has timed out but is still stored - just in case */
2015-09-28 05:07:00 +00:00
num_timeouts - - ;
immediate = 0 ;
}
if ( immediate ) {
/* If this was an immediate response we can use it to get
more detailed connection statistics like RTT .
This lets us determine and adjust server lazy response time
during the session much more accurately . */
timersub ( & now , & pending_queries [ i ] . time , & rtt ) ;
2015-10-03 14:09:34 +00:00
rtt_total_ms + = timeval_to_ms ( & rtt ) ;
2015-09-28 05:07:00 +00:00
num_immediate + + ;
2015-10-04 14:13:47 +00:00
if ( autodetect_server_timeout )
update_server_timeout ( dns_fd , 0 ) ;
2015-09-28 05:07:00 +00:00
}
2015-10-03 14:09:34 +00:00
/* Remove query info from buffer to mark it as answered */
pending_queries [ i ] . id = - 1 ;
pending_queries [ i ] . time . tv_sec = 0 ;
2015-09-28 05:07:00 +00:00
break ;
}
}
2015-10-03 14:09:34 +00:00
if ( id > 0 ) {
QTRACK_DEBUG ( 4 , " got untracked response to id %d. " , id ) ;
2015-09-28 05:07:00 +00:00
num_untracked + + ;
2015-10-03 14:09:34 +00:00
}
2015-09-28 05:07:00 +00:00
}
static int
2015-08-29 12:11:21 +00:00
send_query ( int fd , uint8_t * hostname )
2015-09-28 05:07:00 +00:00
/* Returns DNS ID of sent query */
2009-08-16 12:30:26 +00:00
{
2015-08-29 12:11:21 +00:00
uint8_t packet [ 4096 ] ;
2009-08-16 12:30:26 +00:00
struct query q ;
size_t len ;
2015-10-04 14:39:40 +00:00
DEBUG ( 3 , " TX: pkt len %lu: hostname '%s' " , strlen ( ( char * ) hostname ) , hostname ) ;
2015-08-29 12:11:21 +00:00
2009-09-20 15:11:14 +00:00
chunkid + = 7727 ;
if ( chunkid = = 0 )
/* 0 is used as "no-query" in iodined.c */
2015-09-28 05:07:00 +00:00
chunkid = rand ( ) & 0xFF ;
2009-09-20 15:11:14 +00:00
q . id = chunkid ;
q . type = do_qtype ;
2009-08-16 12:30:26 +00:00
2015-08-29 12:11:21 +00:00
len = dns_encode ( ( char * ) packet , sizeof ( packet ) , & q , QR_QUERY , ( char * ) hostname , strlen ( ( char * ) hostname ) ) ;
2009-09-20 15:11:14 +00:00
if ( len < 1 ) {
warnx ( " dns_encode doesn't fit " ) ;
2015-09-28 05:07:00 +00:00
return - 1 ;
2009-09-20 15:11:14 +00:00
}
2009-08-16 12:30:26 +00:00
2015-10-04 14:39:40 +00:00
DEBUG ( 4 , " Sendquery: id %5d name[0] '%c' " , q . id , hostname [ 0 ] ) ;
2009-12-29 20:00:57 +00:00
2015-08-14 14:07:48 +00:00
sendto ( fd , packet , len , 0 , ( struct sockaddr * ) & nameserv_addrs [ current_nameserver ] ,
sizeof ( struct sockaddr_storage ) ) ;
client_rotate_nameserver ( ) ;
2009-12-29 20:00:57 +00:00
/* There are DNS relays that time out quickly but don't send anything
back on timeout .
And there are relays where , in lazy mode , our new query apparently
_replaces_ our previous query , and we get no answers at all in
lazy mode while legacy immediate - ping - pong works just fine .
Here we detect and fix these situations .
( Can ' t very well do this anywhere else ; this is the only place
2015-09-28 05:07:00 +00:00
we ' ll reliably get to in such situations . ) */
2009-12-29 20:00:57 +00:00
2015-10-04 14:13:47 +00:00
num_sent + + ;
2009-12-29 20:00:57 +00:00
if ( send_query_sendcnt > = 0 & & send_query_sendcnt < 100 & & lazymode ) {
send_query_sendcnt + + ;
2015-10-04 14:13:47 +00:00
if ( ( send_query_sendcnt > windowsize_down & & send_query_recvcnt < = 0 ) | |
( send_query_sendcnt > 2 * windowsize_down & & 4 * send_query_recvcnt < send_query_sendcnt ) ) {
if ( max_timeout_ms > 500 & & autodetect_server_timeout ) {
max_timeout_ms - = 500 ;
double secs = ( double ) max_timeout_ms / 1000.0 ;
fprintf ( stderr , " Receiving too few answers. Setting target timeout to %.1fs (-I%.1f) \n " , secs , secs ) ;
2009-12-29 20:00:57 +00:00
/* restart counting */
send_query_sendcnt = 0 ;
send_query_recvcnt = 0 ;
2015-10-04 14:13:47 +00:00
if ( rtt_total_ms / num_immediate < 1000 ) {
rtt_total_ms = 1000 ;
num_immediate = 1 ;
}
2015-09-28 05:07:00 +00:00
} else if ( lazymode & & autodetect_server_timeout ) {
fprintf ( stderr , " Receiving too few answers. Will try to switch lazy mode off, but that may not "
2015-10-04 14:39:40 +00:00
" always work any more. Start with -L0 next time on this network. \n " ) ;
2009-12-29 20:00:57 +00:00
lazymode = 0 ;
2015-09-28 05:07:00 +00:00
server_timeout_ms = 0 ;
2009-12-29 20:00:57 +00:00
}
2015-10-04 14:13:47 +00:00
update_server_timeout ( fd , 1 ) ;
2009-12-29 20:00:57 +00:00
}
}
2015-09-28 05:07:00 +00:00
return q . id ;
2009-08-16 12:30:26 +00:00
}
static void
2015-09-28 05:07:00 +00:00
send_raw ( int fd , uint8_t * buf , size_t buflen , int user , int cmd )
2009-08-16 12:30:26 +00:00
{
char packet [ 4096 ] ;
int len ;
len = MIN ( sizeof ( packet ) - RAW_HDR_LEN , buflen ) ;
memcpy ( packet , raw_header , RAW_HDR_LEN ) ;
if ( len ) {
memcpy ( & packet [ RAW_HDR_LEN ] , buf , len ) ;
}
len + = RAW_HDR_LEN ;
2015-09-28 05:07:00 +00:00
packet [ RAW_HDR_CMD ] = ( cmd & 0xF0 ) | ( user & 0x0F ) ;
2009-08-16 12:30:26 +00:00
sendto ( fd , packet , len , 0 , ( struct sockaddr * ) & raw_serv , sizeof ( raw_serv ) ) ;
}
static void
2015-09-28 05:07:00 +00:00
send_raw_data ( int dns_fd , uint8_t * data , size_t datalen )
2009-08-16 12:30:26 +00:00
{
2015-09-28 05:07:00 +00:00
send_raw ( dns_fd , data , datalen , userid , RAW_HDR_CMD_DATA ) ;
2009-08-16 12:30:26 +00:00
}
2015-09-28 05:07:00 +00:00
static int
2015-08-29 12:11:21 +00:00
send_packet ( int fd , char cmd , const uint8_t * data , const size_t datalen )
2015-09-28 05:07:00 +00:00
/* Base32 encodes data and sends as single DNS query
* Returns ID of sent query */
2009-08-16 12:30:26 +00:00
{
2015-08-29 12:11:21 +00:00
uint8_t buf [ 4096 ] ;
2009-08-16 12:30:26 +00:00
buf [ 0 ] = cmd ;
2014-06-01 06:34:18 +00:00
2015-08-23 14:14:27 +00:00
build_hostname ( buf , sizeof ( buf ) , data , datalen , topdomain , b32 , hostname_maxlen , 1 ) ;
2015-08-29 12:11:21 +00:00
2015-09-28 05:07:00 +00:00
return send_query ( fd , buf ) ;
2009-08-16 12:30:26 +00:00
}
2015-09-28 05:07:00 +00:00
void
send_ping ( int fd , int ping_response , int ack )
2015-08-21 08:57:54 +00:00
{
2015-10-04 14:13:47 +00:00
num_pings + + ;
2015-08-21 08:57:54 +00:00
if ( conn = = CONN_DNS_NULL ) {
2015-09-28 05:07:00 +00:00
uint8_t data [ 11 ] ;
int id ;
2015-08-21 08:57:54 +00:00
2015-09-28 05:07:00 +00:00
/* Build ping header (see doc/proto_xxxxxxxx.txt) */
2015-08-21 08:57:54 +00:00
data [ 0 ] = userid ;
2015-08-29 12:11:21 +00:00
data [ 1 ] = ack & 0xFF ;
2015-09-28 05:07:00 +00:00
if ( outbuf & & inbuf ) {
data [ 2 ] = outbuf - > windowsize & 0xff ; /* Upstream window size */
data [ 4 ] = inbuf - > windowsize & 0xff ; /* Downstream window size */
data [ 3 ] = outbuf - > start_seq_id & 0xff ; /* Upstream window start */
data [ 5 ] = inbuf - > start_seq_id & 0xff ; /* Downstream window start */
}
* ( uint16_t * ) ( data + 6 ) = htons ( server_timeout_ms ) ;
/* update server lazy timeout, ack flag, respond with ping flag */
data [ 8 ] = ( 1 < < 3 ) | ( ( ack < 0 ? 0 : 1 ) < < 2 ) | ( ping_response & 1 ) ;
data [ 9 ] = ( rand_seed > > 8 ) & 0xff ;
data [ 10 ] = ( rand_seed > > 0 ) & 0xff ;
2015-08-29 12:11:21 +00:00
rand_seed + = 263 ;
2015-08-21 08:57:54 +00:00
2015-10-04 14:39:40 +00:00
DEBUG ( 3 , " SEND PING: respond %d, ack %d, servertimeout %ld, flags %02X " ,
2015-09-28 05:07:00 +00:00
ping_response , ack , server_timeout_ms , data [ 8 ] ) ;
id = send_packet ( fd , ' p ' , data , sizeof ( data ) ) ;
/* Log query ID as being sent now */
query_sent_now ( id ) ;
2015-08-21 08:57:54 +00:00
} else {
send_raw ( fd , NULL , 0 , userid , RAW_HDR_CMD_PING ) ;
}
}
2015-08-21 03:08:47 +00:00
2009-08-16 12:30:26 +00:00
static void
2015-08-21 03:08:47 +00:00
send_next_frag ( int fd )
/* Sends next available fragment of data from the outgoing window buffer */
2009-08-16 12:30:26 +00:00
{
2015-08-29 12:11:21 +00:00
static uint8_t buf [ MAX_FRAGSIZE ] , hdr [ 5 ] ;
2015-09-28 05:07:00 +00:00
int code , id ;
2009-12-29 20:00:57 +00:00
static int datacmc = 0 ;
2015-08-21 03:08:47 +00:00
static char * datacmcchars = " abcdefghijklmnopqrstuvwxyz0123456789 " ;
fragment * f ;
2015-08-29 12:11:21 +00:00
size_t buflen , len ;
2009-08-16 12:30:26 +00:00
2015-08-21 03:08:47 +00:00
/* Get next fragment to send */
2015-08-29 12:11:21 +00:00
f = window_get_next_sending_fragment ( outbuf , & next_downstream_ack ) ;
if ( ! f ) {
2015-09-28 05:07:00 +00:00
if ( outbuf - > numitems > 0 ) {
2015-08-21 03:08:47 +00:00
/* There is stuff to send but we're out of sync, so send a ping
* to get things back in order and keep the packets flowing */
2015-08-29 12:11:21 +00:00
send_ping ( fd , 1 , next_downstream_ack ) ;
next_downstream_ack = - 1 ;
window_tick ( outbuf ) ;
2015-08-21 03:08:47 +00:00
}
2015-09-28 05:07:00 +00:00
return ; /* nothing to send */
2015-08-21 03:08:47 +00:00
}
2009-08-16 12:30:26 +00:00
/* Build upstream data header (see doc/proto_xxxxxxxx.txt) */
2009-09-20 21:10:44 +00:00
buf [ 0 ] = userid_char ; /* First byte is hex userid */
2014-06-01 06:34:18 +00:00
2015-08-21 03:08:47 +00:00
buf [ 1 ] = datacmcchars [ datacmc ] ; /* Second byte is data-CMC */
2009-08-16 12:30:26 +00:00
2015-08-29 12:11:21 +00:00
/* Next 3 bytes is seq ID, downstream ACK and flags */
2015-09-28 05:07:00 +00:00
code = ( ( f - > ack_other < 0 ? 0 : 1 ) < < 3 ) | ( f - > compressed < < 2 )
2015-08-29 12:11:21 +00:00
| ( f - > start < < 1 ) | f - > end ;
2009-08-16 12:30:26 +00:00
2015-08-29 12:11:21 +00:00
hdr [ 0 ] = f - > seqID & 0xFF ;
hdr [ 1 ] = f - > ack_other & 0xFF ;
hdr [ 2 ] = code < < 4 ; /* Flags are in upper 4 bits - lower 4 unused */
2015-08-21 03:08:47 +00:00
2015-08-29 12:11:21 +00:00
buflen = sizeof ( buf ) - 1 ;
/* Encode 3 bytes data into 2 bytes after buf */
len = b32 - > encode ( buf + 2 , & buflen , hdr , 3 ) ;
if ( len ! = 5 )
warnx ( " mismatch in encoded upstream header length! expected 5, got %lu " , len ) ;
2015-08-21 03:08:47 +00:00
2015-08-29 12:11:21 +00:00
/* Encode data into buf after header (6 = user + CMC + 4 bytes header) */
build_hostname ( buf , sizeof ( buf ) , f - > data , f - > len , topdomain ,
dataenc , hostname_maxlen , 6 ) ;
2009-12-29 20:00:57 +00:00
datacmc + + ;
if ( datacmc > = 36 )
datacmc = 0 ;
2014-06-01 06:34:18 +00:00
2015-09-28 05:07:00 +00:00
id = send_query ( fd , buf ) ;
/* Log query ID as being sent now */
query_sent_now ( id ) ;
2015-08-21 15:24:53 +00:00
window_tick ( outbuf ) ;
2015-10-04 14:13:47 +00:00
num_frags_sent + + ;
2009-08-16 12:30:26 +00:00
}
2009-09-21 20:06:30 +00:00
static void
2009-12-29 20:00:57 +00:00
write_dns_error ( struct query * q , int ignore_some_errors )
/* This is called from:
1. handshake_waitdns ( ) when already checked that reply fits to our
latest query .
2. tunnel_dns ( ) when already checked that reply is for our ping or data
packet , but not necessarily the most recent ( SERVFAIL mostly comes
after long delay ) .
So ignorable errors are never printed .
*/
2009-09-21 20:06:30 +00:00
{
if ( ! q ) return ;
switch ( q - > rcode ) {
case NOERROR : /* 0 */
2009-12-29 20:00:57 +00:00
if ( ! ignore_some_errors )
warnx ( " Got reply without error, but also without question and/or answer " ) ;
2009-09-21 20:06:30 +00:00
break ;
case FORMERR : /* 1 */
warnx ( " Got FORMERR as reply: server does not understand our request " ) ;
break ;
case SERVFAIL : /* 2 */
2009-12-29 20:00:57 +00:00
if ( ! ignore_some_errors )
warnx ( " Got SERVFAIL as reply: server failed or recursion timeout " ) ;
2009-09-21 20:06:30 +00:00
break ;
case NXDOMAIN : /* 3 */
warnx ( " Got NXDOMAIN as reply: domain does not exist " ) ;
break ;
case NOTIMP : /* 4 */
warnx ( " Got NOTIMP as reply: server does not support our request " ) ;
break ;
case REFUSED : /* 5 */
warnx ( " Got REFUSED as reply " ) ;
break ;
default :
warnx ( " Got RCODE %u as reply " , q - > rcode ) ;
break ;
}
}
2015-08-29 12:11:21 +00:00
static size_t
dns_namedec ( uint8_t * outdata , size_t outdatalen , uint8_t * buf , size_t buflen )
2009-12-29 20:00:57 +00:00
/* Decodes *buf to *outdata.
* * buf WILL be changed by undotify .
* Note : buflen must be _exactly_ strlen ( buf ) before undotifying .
* ( undotify of reduced - len won ' t copy \ 0 , base - X decode will decode too much . )
* Returns # bytes usefully filled in outdata .
*/
{
size_t outdatalenu = outdatalen ;
switch ( buf [ 0 ] ) {
case ' h ' : /* Hostname with base32 */
case ' H ' :
/* Need 1 byte H, 3 bytes ".xy", >=1 byte data */
if ( buflen < 5 )
return 0 ;
/* this also does undotify */
2015-08-29 12:11:21 +00:00
return unpack_data ( outdata , outdatalen , buf + 1 , buflen - 4 , b32 ) ;
2009-12-29 20:00:57 +00:00
case ' i ' : /* Hostname++ with base64 */
case ' I ' :
/* Need 1 byte I, 3 bytes ".xy", >=1 byte data */
if ( buflen < 5 )
return 0 ;
/* this also does undotify */
2015-08-29 12:11:21 +00:00
return unpack_data ( outdata , outdatalen , buf + 1 , buflen - 4 , b64 ) ;
2009-12-29 20:00:57 +00:00
case ' j ' : /* Hostname++ with base64u */
case ' J ' :
/* Need 1 byte J, 3 bytes ".xy", >=1 byte data */
if ( buflen < 5 )
return 0 ;
/* this also does undotify */
2015-08-29 12:11:21 +00:00
return unpack_data ( outdata , outdatalen , buf + 1 , buflen - 4 , b64u ) ;
2009-12-29 20:00:57 +00:00
case ' k ' : /* Hostname++ with base128 */
case ' K ' :
/* Need 1 byte J, 3 bytes ".xy", >=1 byte data */
if ( buflen < 5 )
return 0 ;
/* this also does undotify */
2015-08-29 12:11:21 +00:00
return unpack_data ( outdata , outdatalen , buf + 1 , buflen - 4 , b128 ) ;
2009-12-29 20:00:57 +00:00
case ' t ' : /* plain base32(Thirty-two) from TXT */
case ' T ' :
if ( buflen < 2 )
return 0 ;
return b32 - > decode ( outdata , & outdatalenu , buf + 1 , buflen - 1 ) ;
case ' s ' : /* plain base64(Sixty-four) from TXT */
case ' S ' :
if ( buflen < 2 )
return 0 ;
return b64 - > decode ( outdata , & outdatalenu , buf + 1 , buflen - 1 ) ;
case ' u ' : /* plain base64u (Underscore) from TXT */
case ' U ' :
if ( buflen < 2 )
return 0 ;
return b64u - > decode ( outdata , & outdatalenu , buf + 1 , buflen - 1 ) ;
case ' v ' : /* plain base128 from TXT */
case ' V ' :
if ( buflen < 2 )
return 0 ;
return b128 - > decode ( outdata , & outdatalenu , buf + 1 , buflen - 1 ) ;
case ' r ' : /* Raw binary from TXT */
case ' R ' :
/* buflen>=1 already checked */
buflen - - ;
buflen = MIN ( buflen , outdatalen ) ;
memcpy ( outdata , buf + 1 , buflen ) ;
return buflen ;
default :
warnx ( " Received unsupported encoding " ) ;
return 0 ;
}
/* notreached */
return 0 ;
}
static int
2015-08-29 12:11:21 +00:00
read_dns_withq ( int dns_fd , int tun_fd , uint8_t * buf , size_t buflen , struct query * q )
2009-12-29 20:00:57 +00:00
/* Returns -1 on receive error or decode error, including DNS error replies.
Returns 0 on replies that could be correct but are useless , and are not
DNS error replies .
Returns > 0 on correct replies ; value is # valid bytes in * buf .
*/
2009-08-16 12:30:26 +00:00
{
2014-02-05 21:36:53 +00:00
struct sockaddr_storage from ;
2015-08-29 12:11:21 +00:00
uint8_t data [ 64 * 1024 ] ;
2009-08-16 12:30:26 +00:00
socklen_t addrlen ;
int r ;
2014-02-05 21:36:53 +00:00
addrlen = sizeof ( from ) ;
2014-06-01 06:34:18 +00:00
if ( ( r = recvfrom ( dns_fd , data , sizeof ( data ) , 0 ,
2009-09-20 21:10:44 +00:00
( struct sockaddr * ) & from , & addrlen ) ) < 0 ) {
2009-08-16 12:30:26 +00:00
warn ( " recvfrom " ) ;
2009-09-20 21:10:44 +00:00
return - 1 ;
2009-08-16 12:30:26 +00:00
}
if ( conn = = CONN_DNS_NULL ) {
int rv ;
2009-09-20 21:10:44 +00:00
if ( r < = 0 )
/* useless packet */
return 0 ;
2009-08-16 12:30:26 +00:00
2015-08-29 12:11:21 +00:00
rv = dns_decode ( ( char * ) buf , buflen , q , QR_ANSWER , ( char * ) data , r ) ;
2009-09-20 21:10:44 +00:00
if ( rv < = 0 )
return rv ;
2009-08-16 12:30:26 +00:00
2009-12-29 20:00:57 +00:00
if ( q - > type = = T_CNAME | | q - > type = = T_TXT )
/* CNAME can also be returned from an A question */
2009-09-20 15:11:14 +00:00
{
/*
* buf is a hostname or txt stream that we still need to
* decode to binary
2014-06-01 06:34:18 +00:00
*
2009-09-20 15:11:14 +00:00
* also update rv with the number of valid bytes
2014-06-01 06:34:18 +00:00
*
2009-09-20 15:11:14 +00:00
* data is unused here , and will certainly hold the smaller binary
*/
2009-12-29 20:00:57 +00:00
rv = dns_namedec ( data , sizeof ( data ) , buf , rv ) ;
2009-09-20 15:11:14 +00:00
2009-12-29 20:00:57 +00:00
rv = MIN ( rv , buflen ) ;
if ( rv > 0 )
2009-09-20 15:11:14 +00:00
memcpy ( buf , data , rv ) ;
2009-12-29 20:00:57 +00:00
} else if ( q - > type = = T_MX | | q - > type = = T_SRV ) {
/* buf is like "Hname.com\0Hanother.com\0\0" */
int buftotal = rv ; /* idx of last \0 */
int bufoffset = 0 ;
int dataoffset = 0 ;
int thispartlen , dataspace , datanew ;
while ( 1 ) {
2015-08-29 12:11:21 +00:00
thispartlen = strlen ( ( char * ) buf ) ;
2009-12-29 20:00:57 +00:00
thispartlen = MIN ( thispartlen , buftotal - bufoffset ) ;
dataspace = sizeof ( data ) - dataoffset ;
if ( thispartlen < = 0 | | dataspace < = 0 )
2009-09-20 15:11:14 +00:00
break ;
2009-12-29 20:00:57 +00:00
datanew = dns_namedec ( data + dataoffset , dataspace ,
buf + bufoffset , thispartlen ) ;
if ( datanew < = 0 )
2009-09-20 15:11:14 +00:00
break ;
2009-12-29 20:00:57 +00:00
bufoffset + = thispartlen + 1 ;
dataoffset + = datanew ;
2009-09-20 15:11:14 +00:00
}
2009-12-29 20:00:57 +00:00
rv = dataoffset ;
rv = MIN ( rv , buflen ) ;
if ( rv > 0 )
memcpy ( buf , data , rv ) ;
2009-09-20 15:11:14 +00:00
}
2015-10-04 14:39:40 +00:00
DEBUG ( 2 , " RX: id %5d name[0]='%c' " , q - > id , q - > name [ 0 ] ) ;
2015-09-28 05:07:00 +00:00
2009-08-16 12:30:26 +00:00
return rv ;
} else { /* CONN_RAW_UDP */
2015-09-28 05:07:00 +00:00
size_t datalen ;
uint8_t buf [ 64 * 1024 ] ;
2009-08-16 12:30:26 +00:00
/* minimum length */
2015-09-28 05:07:00 +00:00
if ( r < RAW_HDR_LEN )
return 0 ;
2009-08-16 12:30:26 +00:00
/* should start with header */
2015-09-28 05:07:00 +00:00
if ( memcmp ( data , raw_header , RAW_HDR_IDENT_LEN ) )
return 0 ;
2009-08-16 12:30:26 +00:00
/* should be my user id */
2015-09-28 05:07:00 +00:00
if ( RAW_HDR_GET_USR ( data ) ! = userid )
return 0 ;
2009-08-16 12:30:26 +00:00
2009-12-29 20:00:57 +00:00
if ( RAW_HDR_GET_CMD ( data ) = = RAW_HDR_CMD_DATA | |
RAW_HDR_GET_CMD ( data ) = = RAW_HDR_CMD_PING )
lastdownstreamtime = time ( NULL ) ;
/* should be data packet */
2015-09-28 05:07:00 +00:00
if ( RAW_HDR_GET_CMD ( data ) ! = RAW_HDR_CMD_DATA )
return 0 ;
2009-12-29 20:00:57 +00:00
2009-08-16 12:30:26 +00:00
r - = RAW_HDR_LEN ;
datalen = sizeof ( buf ) ;
2015-09-28 05:07:00 +00:00
if ( uncompress ( buf , & datalen , data + RAW_HDR_LEN , r ) = = Z_OK ) {
write_tun ( tun_fd , buf , datalen ) ;
2009-08-16 12:30:26 +00:00
}
2009-12-29 20:00:57 +00:00
2015-09-28 05:07:00 +00:00
/* all done */
2009-08-16 12:30:26 +00:00
return 0 ;
}
}
2015-10-04 14:13:47 +00:00
int
2015-09-28 05:07:00 +00:00
handshake_waitdns ( int dns_fd , char * buf , size_t buflen , char cmd , int timeout )
2009-12-29 20:00:57 +00:00
/* Wait for DNS reply fitting to our latest query and returns it.
Returns length of reply = # bytes used in buf .
Returns 0 if fitting reply happens to be useless .
Returns - 2 on ( at least ) DNS error that fits to our latest query ,
error message already printed .
Returns - 3 on timeout ( given in seconds ) .
Returns - 1 on other errors .
Timeout is restarted when " wrong " ( previous / delayed ) replies are received ,
so effective timeout may be longer than specified .
2009-09-20 21:10:44 +00:00
*/
{
struct query q ;
2009-12-29 20:00:57 +00:00
int r , rv ;
fd_set fds ;
struct timeval tv ;
2015-09-28 05:07:00 +00:00
char qcmd ;
cmd = toupper ( cmd ) ;
2009-12-29 20:00:57 +00:00
while ( 1 ) {
tv . tv_sec = timeout ;
tv . tv_usec = 0 ;
FD_ZERO ( & fds ) ;
FD_SET ( dns_fd , & fds ) ;
r = select ( dns_fd + 1 , & fds , NULL , NULL , & tv ) ;
2009-09-20 21:10:44 +00:00
2009-12-29 20:00:57 +00:00
if ( r < 0 )
return - 1 ; /* select error */
if ( r = = 0 )
return - 3 ; /* select timeout */
2009-09-20 21:10:44 +00:00
2009-12-29 20:00:57 +00:00
q . id = 0 ;
q . name [ 0 ] = ' \0 ' ;
2015-08-29 12:11:21 +00:00
rv = read_dns_withq ( dns_fd , 0 , ( uint8_t * ) buf , buflen , & q ) ;
2009-12-29 20:00:57 +00:00
2015-09-28 05:07:00 +00:00
qcmd = toupper ( q . name [ 0 ] ) ;
if ( q . id ! = chunkid | | qcmd ! = cmd ) {
2015-10-04 14:39:40 +00:00
DEBUG ( 1 , " Ignoring unfitting reply id %d starting with '%c' " , q . id , q . name [ 0 ] ) ;
2009-12-29 20:00:57 +00:00
continue ;
}
/* if still here: reply matches our latest query */
/* Non-recursive DNS servers (such as [a-m].root-servers.net)
return no answer , but only additional and authority records .
Can ' t explicitly test for that here , just assume that
NOERROR is such situation . Only trigger on the very first
requests ( Y or V , depending if - T given ) .
*/
if ( rv < 0 & & q . rcode = = NOERROR & &
( q . name [ 0 ] = = ' Y ' | | q . name [ 0 ] = = ' y ' | |
q . name [ 0 ] = = ' V ' | | q . name [ 0 ] = = ' v ' ) ) {
fprintf ( stderr , " Got empty reply. This nameserver may not be resolving recursively, use another. \n " ) ;
fprintf ( stderr , " Try \" iodine [options] ns.%s %s \" first, it might just work. \n " ,
topdomain , topdomain ) ;
return - 2 ;
}
/* If we get an immediate SERVFAIL on the handshake query
we ' re waiting for , wait a while before sending the next .
SERVFAIL reliably happens during fragsize autoprobe , but
mostly long after we ' ve moved along to some other queries .
However , some DNS relays , once they throw a SERVFAIL , will
for several seconds apply it immediately to _any_ new query
for the same topdomain . When this happens , waiting a while
is the only option that works .
*/
if ( rv < 0 & & q . rcode = = SERVFAIL )
sleep ( 1 ) ;
if ( rv < 0 ) {
write_dns_error ( & q , 1 ) ;
return - 2 ;
}
/* rv either 0 or >0, return it as is. */
return rv ;
2009-09-21 20:06:30 +00:00
}
2009-12-29 20:00:57 +00:00
/* not reached */
return - 1 ;
2009-09-20 21:10:44 +00:00
}
2009-08-16 12:30:26 +00:00
2015-10-04 14:13:47 +00:00
int
2015-09-28 05:07:00 +00:00
parse_data ( uint8_t * data , size_t len , fragment * f , int * immediate )
2015-08-21 03:08:47 +00:00
{
2015-08-29 12:11:21 +00:00
size_t headerlen = DOWNSTREAM_HDR ;
2015-09-28 05:07:00 +00:00
int ping ;
2015-08-21 03:08:47 +00:00
memset ( f , 0 , sizeof ( fragment ) ) ;
2015-09-28 05:07:00 +00:00
2015-08-29 12:11:21 +00:00
f - > seqID = data [ 0 ] ;
2015-09-28 05:07:00 +00:00
/* Flags */
f - > end = data [ 2 ] & 1 ;
f - > start = ( data [ 2 ] > > 1 ) & 1 ;
f - > compressed = ( data [ 2 ] > > 2 ) & 1 ;
f - > ack_other = ( data [ 2 ] > > 3 ) & 1 ? data [ 1 ] : - 1 ;
ping = ( data [ 2 ] > > 4 ) & 1 ;
if ( immediate )
* immediate = ( data [ 2 ] > > 5 ) & 1 ;
2015-08-29 12:11:21 +00:00
if ( ping ) { /* Handle ping stuff */
2015-09-28 05:07:00 +00:00
static unsigned dn_start_seq , up_start_seq , dn_wsize , up_wsize ;
2015-08-29 12:11:21 +00:00
headerlen = DOWNSTREAM_PING_HDR ;
if ( len < headerlen ) return - 1 ; /* invalid packet - continue */
2015-09-28 05:07:00 +00:00
2015-08-29 12:11:21 +00:00
/* Parse data/ping header */
2015-09-28 05:07:00 +00:00
dn_wsize = data [ 3 ] ;
2015-08-29 12:11:21 +00:00
up_wsize = data [ 4 ] ;
dn_start_seq = data [ 5 ] ;
up_start_seq = data [ 6 ] ;
2015-10-04 14:39:40 +00:00
DEBUG ( 3 , " PING pkt data=%lu WS: up=%u, dn=%u; Start: up=%u, dn=%u " ,
2015-08-29 12:11:21 +00:00
len - headerlen , up_wsize , dn_wsize , up_start_seq , dn_start_seq ) ;
2015-08-21 08:57:54 +00:00
}
2015-08-29 12:11:21 +00:00
f - > len = len - headerlen ;
2015-09-28 05:07:00 +00:00
if ( f - > len > 0 )
memcpy ( f - > data , data + headerlen , MIN ( f - > len , sizeof ( f - > data ) ) ) ;
2015-08-21 08:57:54 +00:00
return ping ; /* return ping flag (if corresponding query was a ping) */
2015-08-21 03:08:47 +00:00
}
2009-08-16 12:30:26 +00:00
static int
tunnel_tun ( int tun_fd , int dns_fd )
{
2015-09-28 05:07:00 +00:00
size_t datalen ;
2015-08-21 03:08:47 +00:00
uint8_t out [ 64 * 1024 ] ;
uint8_t in [ 64 * 1024 ] ;
2015-09-28 05:07:00 +00:00
uint8_t * data ;
2009-08-16 12:30:26 +00:00
ssize_t read ;
if ( ( read = read_tun ( tun_fd , in , sizeof ( in ) ) ) < = 0 )
return - 1 ;
2015-08-29 12:11:21 +00:00
/* Check if outgoing buffer can hold data */
if ( window_buffer_available ( outbuf ) < ( read / MAX_FRAGSIZE ) + 1 ) {
2015-10-04 14:39:40 +00:00
DEBUG ( 1 , " Outgoing buffer full (%lu/%lu), not adding data! " ,
2015-10-03 14:09:34 +00:00
outbuf - > numitems , outbuf - > length ) ;
2009-09-20 21:10:44 +00:00
return - 1 ;
2015-08-29 12:11:21 +00:00
}
2015-10-04 14:39:40 +00:00
DEBUG ( 2 , " IN: %lu bytes on tunnel, compression %d " , read , compression_up ) ;
2009-08-16 12:30:26 +00:00
2015-09-28 05:07:00 +00:00
if ( conn ! = CONN_DNS_NULL | | compression_up ) {
datalen = sizeof ( out ) ;
compress2 ( out , & datalen , in , read , 9 ) ;
data = out ;
} else {
datalen = read ;
data = in ;
}
2009-08-16 12:30:26 +00:00
if ( conn = = CONN_DNS_NULL ) {
2015-09-28 05:07:00 +00:00
window_add_outgoing_data ( outbuf , data , datalen , compression_up ) ;
2015-08-21 03:08:47 +00:00
send_next_frag ( dns_fd ) ;
2009-08-16 12:30:26 +00:00
} else {
2015-09-28 05:07:00 +00:00
send_raw_data ( dns_fd , data , datalen ) ;
2009-08-16 12:30:26 +00:00
}
return read ;
}
static int
tunnel_dns ( int tun_fd , int dns_fd )
{
2015-08-14 14:07:48 +00:00
static struct query q ;
2015-08-29 12:11:21 +00:00
size_t datalen , buflen ;
2015-10-04 14:13:47 +00:00
static uint8_t buf [ 64 * 1024 ] , cbuf [ 64 * 1024 ] , * data ;
2015-08-21 03:08:47 +00:00
static fragment f ;
2015-09-28 05:07:00 +00:00
int read , compressed , res , immediate ;
2009-08-16 12:30:26 +00:00
2015-08-21 03:08:47 +00:00
memset ( & q , 0 , sizeof ( q ) ) ;
2015-08-14 14:07:48 +00:00
memset ( buf , 0 , sizeof ( buf ) ) ;
2015-08-21 03:08:47 +00:00
memset ( cbuf , 0 , sizeof ( cbuf ) ) ;
2015-08-29 12:11:21 +00:00
read = read_dns_withq ( dns_fd , tun_fd , cbuf , sizeof ( cbuf ) , & q ) ;
2009-09-21 20:06:30 +00:00
2009-12-29 20:00:57 +00:00
if ( conn ! = CONN_DNS_NULL )
return 1 ; /* everything already done */
/* Don't process anything that isn't data for us; usually error
replies from fragsize probes etc . However a sequence of those ,
mostly 1 sec apart , will continuously break the > = 2 - second select
timeout , which means we won ' t send a proper ping for a while .
So make select a bit faster , < 1 sec . */
2009-09-21 20:06:30 +00:00
if ( q . name [ 0 ] ! = ' P ' & & q . name [ 0 ] ! = ' p ' & &
2009-12-29 20:00:57 +00:00
q . name [ 0 ] ! = userid_char & & q . name [ 0 ] ! = userid_char2 ) {
send_ping_soon = 700 ;
2009-09-21 20:06:30 +00:00
return - 1 ; /* nothing done */
2009-12-29 20:00:57 +00:00
}
2009-09-21 20:06:30 +00:00
2015-08-29 12:11:21 +00:00
if ( read < DOWNSTREAM_HDR ) {
2009-09-20 21:10:44 +00:00
/* Maybe SERVFAIL etc. Send ping to get things back in order,
but wait a bit to prevent fast ping - pong loops . */
2009-12-29 20:00:57 +00:00
if ( read < 0 )
write_dns_error ( & q , 0 ) ;
2015-10-04 14:13:47 +00:00
if ( read < 0 & & q . rcode = = SERVFAIL & & lazymode & & max_timeout_ms > 500 ) {
num_servfail + + ;
if ( send_query_recvcnt < 500 & & num_servfail < 4 ) {
fprintf ( stderr , " Hmm, that's %ld SERVFAILs. Your data should still go through... " , num_servfail ) ;
} else if ( send_query_recvcnt < 500 & & num_servfail > = 4 & & autodetect_server_timeout ) {
2015-09-28 05:07:00 +00:00
server_timeout_ms - = 500 ;
double server_timeout = ( float ) server_timeout_ms / 1000.0 ;
2015-10-04 14:13:47 +00:00
fprintf ( stderr , " Exceeded acceptable limit for SERVFAILs (%ld), setting "
" timeout to %.1f secs. (use -I%.1f next time on this network) \n " ,
num_servfail , server_timeout , server_timeout ) ;
/* Reset query counts stats */
2009-12-29 20:00:57 +00:00
send_query_sendcnt = 0 ;
send_query_recvcnt = 0 ;
2015-10-04 14:13:47 +00:00
if ( rtt_total_ms / num_immediate < 1000 ) {
/* only reset avg RTT if it might be too small (TODO trust the statistics) */
rtt_total_ms = 1000 ;
num_immediate = 1 ;
}
update_server_timeout ( dns_fd , 1 ) ;
2009-12-29 20:00:57 +00:00
}
}
2009-09-20 21:10:44 +00:00
send_ping_soon = 900 ;
return - 1 ; /* nothing done */
}
2009-08-16 12:30:26 +00:00
2015-08-21 03:08:47 +00:00
if ( read = = 5 & & ! strncmp ( " BADIP " , ( char * ) cbuf , 5 ) ) {
2015-10-04 14:13:47 +00:00
num_badip + + ;
if ( num_badip % 5 = = 1 ) {
fprintf ( stderr , " BADIP (%ld): Server rejected sender IP address (maybe iodined -c will help), or server "
" kicked us due to timeout. Will exit if no downstream data is received in 60 seconds. " , num_badip ) ;
}
2009-09-20 21:10:44 +00:00
return - 1 ; /* nothing done */
2009-08-16 12:30:26 +00:00
}
2015-08-29 12:11:21 +00:00
/* Okay, we have a recent downstream packet */
lastdownstreamtime = time ( NULL ) ;
2015-09-28 05:07:00 +00:00
2015-10-04 14:13:47 +00:00
send_query_recvcnt + + ; /* unlikely we will ever overflow (2^64 queries is a LOT) */
num_recv + + ;
2015-08-29 12:11:21 +00:00
2015-08-21 03:08:47 +00:00
/* Decode the downstream data header and fragment-ify ready for processing */
2015-09-28 05:07:00 +00:00
res = parse_data ( cbuf , read , & f , & immediate ) ;
2015-08-29 12:11:21 +00:00
if ( ( debug > = 3 & & res ) | | ( debug > = 2 & & ! res ) )
2015-09-28 05:07:00 +00:00
fprintf ( stderr , " RX %s; frag ID %3u, ACK %3d, compression %d, datalen %lu, s%d e%d \n " ,
res ? " PING " : " DATA " , f . seqID , f . ack_other , f . compressed , f . len , f . start , f . end ) ;
/* Mark query as received */
2015-10-04 14:13:47 +00:00
got_response ( q . id , immediate , dns_fd ) ;
2009-09-20 21:10:44 +00:00
2015-08-21 03:08:47 +00:00
window_ack ( outbuf , f . ack_other ) ;
2015-10-03 14:09:34 +00:00
window_tick ( outbuf ) ;
2009-09-20 21:10:44 +00:00
2015-08-21 03:08:47 +00:00
/* In lazy mode, we shouldn't get immediate replies to our most-recent
query , only during heavy data transfer . Since this means the server
doesn ' t have any packets to send , send one relatively fast ( but not
too fast , to avoid runaway ping - pong loops . . ) */
2015-08-29 12:11:21 +00:00
/* Don't send anything too soon; no data waiting from server */
2015-08-21 03:08:47 +00:00
if ( f . len = = 0 ) {
2015-10-04 14:39:40 +00:00
if ( ! res )
DEBUG ( 1 , " [WARNING] Received downstream data fragment with 0 length and NOT a ping! " ) ;
2015-09-28 05:07:00 +00:00
if ( ! lazymode )
send_ping_soon = 100 ;
else
send_ping_soon = 700 ;
2015-08-21 03:08:47 +00:00
return - 1 ;
2009-09-20 21:10:44 +00:00
}
2015-09-28 05:07:00 +00:00
/* Get next ACK if nothing already pending: if we get a new ack
* then we must send it immediately . */
if ( next_downstream_ack > = 0 ) {
/* If this happens something is wrong (or last frag was a re-send)
* May result in ACKs being delayed . */
2015-10-04 14:39:40 +00:00
DEBUG ( 1 , " next_downstream_ack NOT -1! (%d), %u resends, %u oos " , next_downstream_ack , outbuf - > resends , outbuf - > oos ) ;
2015-08-29 12:11:21 +00:00
}
2009-09-20 21:10:44 +00:00
2015-09-28 05:07:00 +00:00
/* Downstream data traffic + get ack for that data */
next_downstream_ack = window_process_incoming_fragment ( inbuf , & f ) ;
2009-09-20 21:10:44 +00:00
2015-10-04 14:13:47 +00:00
num_frags_recv + + ;
2015-08-21 03:08:47 +00:00
datalen = window_reassemble_data ( inbuf , cbuf , sizeof ( cbuf ) , & compressed ) ;
2015-09-28 05:07:00 +00:00
if ( datalen > 0 ) {
2015-08-21 03:08:47 +00:00
if ( compressed ) {
2015-08-29 12:11:21 +00:00
buflen = sizeof ( buf ) ;
if ( ( res = uncompress ( buf , & buflen , cbuf , datalen ) ) ! = Z_OK ) {
2015-10-04 14:39:40 +00:00
DEBUG ( 1 , " Uncompress failed (%d) for data len %lu: reassembled data corrupted or incomplete! " , res , datalen ) ;
2015-09-28 05:07:00 +00:00
datalen = 0 ;
} else {
datalen = buflen ;
2009-09-20 21:10:44 +00:00
}
2015-10-04 14:13:47 +00:00
data = buf ;
} else {
data = cbuf ;
2009-09-20 21:10:44 +00:00
}
2015-09-28 05:07:00 +00:00
if ( datalen )
2015-10-04 14:13:47 +00:00
write_tun ( tun_fd , data , datalen ) ;
2009-09-20 21:10:44 +00:00
}
2015-08-21 03:08:47 +00:00
/* Move window along after doing all data processing */
window_tick ( inbuf ) ;
2009-09-20 21:10:44 +00:00
2009-08-16 12:30:26 +00:00
return read ;
}
int
client_tunnel ( int tun_fd , int dns_fd )
{
struct timeval tv ;
fd_set fds ;
int rv ;
int i ;
2015-10-03 14:09:34 +00:00
int sending ;
2015-10-04 14:13:47 +00:00
time_t last_stats ;
2015-09-28 05:07:00 +00:00
if ( conn ! = CONN_DNS_NULL ) {
compression_up = 1 ;
}
outbuf = window_buffer_init ( 64 , windowsize_up , maxfragsize_up , WINDOW_SENDING ) ;
/* Incoming buffer max fragsize doesn't matter */
inbuf = window_buffer_init ( 64 , windowsize_down , MAX_FRAGSIZE , WINDOW_RECVING ) ;
2015-10-04 14:13:47 +00:00
/* init query tracking */
num_untracked = 0 ;
num_pending = 0 ;
2015-09-28 05:07:00 +00:00
pending_queries = calloc ( PENDING_QUERIES_LENGTH , sizeof ( struct query_tuple ) ) ;
for ( int i = 0 ; i < PENDING_QUERIES_LENGTH ; i + + )
pending_queries [ i ] . id = - 1 ;
2009-08-16 12:30:26 +00:00
2015-09-28 05:07:00 +00:00
/* start counting now */
2009-08-16 12:30:26 +00:00
rv = 0 ;
2009-09-20 21:10:44 +00:00
lastdownstreamtime = time ( NULL ) ;
2015-10-04 14:13:47 +00:00
last_stats = time ( NULL ) ;
2015-09-28 05:07:00 +00:00
rtt_total_ms = 1000 ;
num_immediate = 1 ;
2015-10-04 14:13:47 +00:00
/* reset connection statistics */
num_init_queries = MAX ( send_query_recvcnt , send_query_sendcnt ) ;
num_badip = 0 ;
num_servfail = 0 ;
2015-09-28 05:07:00 +00:00
num_timeouts = 0 ;
send_query_recvcnt = 0 ;
send_query_sendcnt = 0 ;
2015-10-04 14:13:47 +00:00
num_sent = 0 ;
num_recv = 0 ;
num_frags_sent = 0 ;
num_frags_recv = 0 ;
num_pings = 0 ;
2015-09-28 05:07:00 +00:00
2015-10-03 14:09:34 +00:00
if ( debug > = 5 )
2015-09-28 05:07:00 +00:00
window_debug = debug - 3 ;
2009-08-16 12:30:26 +00:00
while ( running ) {
2015-10-03 14:09:34 +00:00
tv = ms_to_timeval ( max_timeout_ms ) ;
2015-09-28 05:07:00 +00:00
2015-10-04 14:13:47 +00:00
/* TODO: detect DNS servers which drop frequent requests
2015-09-28 05:07:00 +00:00
* TODO : adjust number of pending queries based on current data rate */
sending = window_sending ( outbuf ) ;
2015-10-03 14:09:34 +00:00
check_pending_queries ( ) ;
if ( sending | | ( num_pending < windowsize_down & & lazymode ) | | next_downstream_ack > = 0 ) {
2015-09-28 05:07:00 +00:00
/* Upstream data traffic */
2015-10-03 14:09:34 +00:00
if ( sending > 0 ) {
2015-09-28 05:07:00 +00:00
/* More to send - next fragment */
send_next_frag ( dns_fd ) ;
} else {
/* Send ping if we didn't send anything yet */
send_ping ( dns_fd , 0 , next_downstream_ack ) ;
next_downstream_ack = - 1 ;
}
2015-10-03 14:09:34 +00:00
QTRACK_DEBUG ( 3 , " Sent a query to fill server lazy buffer to %lu. " , windowsize_down ) ;
2009-08-16 12:30:26 +00:00
2015-08-29 12:11:21 +00:00
tv . tv_sec = 0 ;
tv . tv_usec = 2000 ;
send_ping_soon = 0 ;
}
2015-10-04 14:13:47 +00:00
if ( stats ) {
if ( difftime ( time ( NULL ) , last_stats ) > = stats ) {
/* print useful statistics report */
fprintf ( stderr , " \n ========== iodine connection statistics (user %1d) ========== \n " , userid ) ;
fprintf ( stderr , " Queries sent: %8lu " " answered: %8lu " " SERVFAILs: %4lu \n " ,
num_sent , num_recv , num_servfail ) ;
fprintf ( stderr , " during init: %4lu IP rejected: %4lu untracked: %4lu \n " ,
num_init_queries , num_badip , num_untracked ) ;
fprintf ( stderr , " Average round-trip time: %4ld ms immediate answers: %5lu \n " ,
rtt_total_ms / num_immediate , num_immediate ) ;
fprintf ( stderr , " query timeouts: %4lu target: %4ld ms server: %4ld ms \n " ,
num_timeouts , max_timeout_ms , server_timeout_ms ) ;
fprintf ( stderr , " Resent fragments up: %4u downstream out of window: %4u \n " ,
outbuf - > resends , inbuf - > oos ) ;
fprintf ( stderr , " TX fragments: %8lu " " RX: %8lu " " pings: %8lu " " \n \n " ,
num_frags_sent , num_frags_recv , num_pings ) ;
last_stats = time ( NULL ) ;
}
}
2009-09-20 21:10:44 +00:00
if ( send_ping_soon ) {
tv . tv_sec = 0 ;
tv . tv_usec = send_ping_soon * 1000 ;
}
2009-08-16 12:30:26 +00:00
FD_ZERO ( & fds ) ;
2015-08-29 12:11:21 +00:00
if ( window_buffer_available ( outbuf ) > 16 ) {
2015-08-21 03:08:47 +00:00
/* Fill up outgoing buffer with available data
2015-08-29 12:11:21 +00:00
* The windowing protocol manages data retransmits , timeouts etc . */
2009-08-16 12:30:26 +00:00
FD_SET ( tun_fd , & fds ) ;
}
FD_SET ( dns_fd , & fds ) ;
i = select ( MAX ( tun_fd , dns_fd ) + 1 , & fds , NULL , NULL , & tv ) ;
2009-09-20 21:10:44 +00:00
2015-09-28 05:07:00 +00:00
if ( difftime ( time ( NULL ) , lastdownstreamtime ) > 60 ) {
2015-10-04 14:13:47 +00:00
fprintf ( stderr , " No downstream data received in 60 seconds, shutting down. \n " ) ;
2009-09-20 21:10:44 +00:00
running = 0 ;
}
2014-06-01 06:34:18 +00:00
2009-08-16 12:30:26 +00:00
if ( running = = 0 )
break ;
2014-06-01 06:34:18 +00:00
if ( i < 0 )
2015-08-21 03:08:47 +00:00
err ( 1 , " select < 0 " ) ;
2009-08-16 12:30:26 +00:00
2009-09-20 21:10:44 +00:00
if ( i = = 0 ) {
2015-10-03 14:09:34 +00:00
/* TODO check number of timeouts and do something about it */
2009-08-16 12:30:26 +00:00
} else {
2009-09-20 21:10:44 +00:00
2009-08-16 12:30:26 +00:00
if ( FD_ISSET ( tun_fd , & fds ) ) {
if ( tunnel_tun ( tun_fd , dns_fd ) < = 0 )
continue ;
2009-09-20 21:10:44 +00:00
/* Returns -1 on error OR when quickly
dropping data in case of DNS congestion ;
we need to _not_ do tunnel_dns ( ) then .
If chunk sent , sets send_ping_soon = 0. */
2009-08-16 12:30:26 +00:00
}
if ( FD_ISSET ( dns_fd , & fds ) ) {
2015-08-21 03:08:47 +00:00
tunnel_dns ( tun_fd , dns_fd ) ;
2014-06-01 06:34:18 +00:00
}
2009-08-16 12:30:26 +00:00
}
}
return rv ;
}
static void
send_login ( int fd , char * login , int len )
{
2015-08-29 12:11:21 +00:00
uint8_t data [ 19 ] ;
2009-08-16 12:30:26 +00:00
memset ( data , 0 , sizeof ( data ) ) ;
data [ 0 ] = userid ;
memcpy ( & data [ 1 ] , login , MIN ( len , 16 ) ) ;
data [ 17 ] = ( rand_seed > > 8 ) & 0xff ;
data [ 18 ] = ( rand_seed > > 0 ) & 0xff ;
2014-06-01 06:34:18 +00:00
2009-08-16 12:30:26 +00:00
rand_seed + + ;
2009-09-20 15:11:14 +00:00
send_packet ( fd , ' l ' , data , sizeof ( data ) ) ;
2009-08-16 12:30:26 +00:00
}
static void
2015-09-28 05:07:00 +00:00
send_fragsize_probe ( int fd , uint16_t fragsize )
2009-08-16 12:30:26 +00:00
{
2015-08-29 12:11:21 +00:00
uint8_t probedata [ 256 ] ;
uint8_t buf [ MAX_FRAGSIZE ] ;
2015-09-28 05:07:00 +00:00
uint8_t hdr [ 3 ] ;
size_t hdr_len_enc = 6 ;
2009-08-16 12:30:26 +00:00
2015-09-28 05:07:00 +00:00
buf [ 0 ] = ' r ' ; /* Probe downstream fragsize packet */
hdr [ 0 ] = userid ;
* ( uint16_t * ) ( hdr + 1 ) = htons ( fragsize ) ;
b32 - > encode ( buf + 1 , & hdr_len_enc , hdr , 3 ) ;
/* build a large query domain which is random and maximum size,
* will also take up maximum space in the return packet */
2009-09-20 15:11:14 +00:00
memset ( probedata , MAX ( 1 , rand_seed & 0xff ) , sizeof ( probedata ) ) ;
probedata [ 1 ] = MAX ( 1 , ( rand_seed > > 8 ) & 0xff ) ;
2009-08-16 12:30:26 +00:00
rand_seed + + ;
2009-12-29 20:00:57 +00:00
/* Note: must either be same, or larger, than send_chunk() */
2015-08-23 14:14:27 +00:00
build_hostname ( buf , sizeof ( buf ) , probedata , sizeof ( probedata ) , topdomain ,
2015-09-28 05:07:00 +00:00
dataenc , hostname_maxlen , 6 ) ;
2009-08-16 12:30:26 +00:00
send_query ( fd , buf ) ;
}
static void
2015-09-28 05:07:00 +00:00
send_set_downstream_fragsize ( int fd , uint16_t fragsize )
2009-08-16 12:30:26 +00:00
{
2015-08-29 12:11:21 +00:00
uint8_t data [ 5 ] ;
2014-06-01 06:34:18 +00:00
2009-08-16 12:30:26 +00:00
data [ 0 ] = userid ;
2015-09-28 05:07:00 +00:00
* ( uint16_t * ) ( data + 1 ) = htons ( fragsize ) ;
2009-08-16 12:30:26 +00:00
data [ 3 ] = ( rand_seed > > 8 ) & 0xff ;
data [ 4 ] = ( rand_seed > > 0 ) & 0xff ;
2014-06-01 06:34:18 +00:00
2009-08-16 12:30:26 +00:00
rand_seed + + ;
2009-09-20 15:11:14 +00:00
send_packet ( fd , ' n ' , data , sizeof ( data ) ) ;
2009-08-16 12:30:26 +00:00
}
2014-06-01 06:34:18 +00:00
static void
2009-08-16 12:30:26 +00:00
send_version ( int fd , uint32_t version )
{
2015-08-29 12:11:21 +00:00
uint8_t data [ 6 ] ;
2014-06-01 06:34:18 +00:00
2015-09-28 05:07:00 +00:00
version = htonl ( version ) ;
* ( uint32_t * ) data = version ;
2009-08-16 12:30:26 +00:00
data [ 4 ] = ( rand_seed > > 8 ) & 0xff ;
data [ 5 ] = ( rand_seed > > 0 ) & 0xff ;
2014-06-01 06:34:18 +00:00
2009-08-16 12:30:26 +00:00
rand_seed + + ;
2009-12-29 20:00:57 +00:00
send_packet ( fd , ' v ' , data , sizeof ( data ) ) ;
2009-08-16 12:30:26 +00:00
}
static void
send_ip_request ( int fd , int userid )
{
2015-08-29 12:11:21 +00:00
uint8_t buf [ 512 ] = " i____. " ;
2009-08-16 12:30:26 +00:00
buf [ 1 ] = b32_5to8 ( userid ) ;
2014-06-01 06:34:18 +00:00
2009-08-16 12:30:26 +00:00
buf [ 2 ] = b32_5to8 ( ( rand_seed > > 10 ) & 0x1f ) ;
buf [ 3 ] = b32_5to8 ( ( rand_seed > > 5 ) & 0x1f ) ;
buf [ 4 ] = b32_5to8 ( ( rand_seed ) & 0x1f ) ;
rand_seed + + ;
2015-08-29 12:11:21 +00:00
strncat ( ( char * ) buf , topdomain , 512 - strlen ( ( char * ) buf ) ) ;
2009-08-16 12:30:26 +00:00
send_query ( fd , buf ) ;
}
static void
send_raw_udp_login ( int dns_fd , int userid , int seed )
{
char buf [ 16 ] ;
login_calculate ( buf , 16 , password , seed + 1 ) ;
2015-09-28 05:07:00 +00:00
send_raw ( dns_fd , ( uint8_t * ) buf , sizeof ( buf ) , userid , RAW_HDR_CMD_LOGIN ) ;
2009-08-16 12:30:26 +00:00
}
static void
2009-12-29 20:00:57 +00:00
send_upenctest ( int fd , char * s )
/* NOTE: String may be at most 63-4=59 chars to fit in 1 dns chunk. */
2009-08-16 12:30:26 +00:00
{
2009-12-29 20:00:57 +00:00
char buf [ 512 ] = " z___ " ;
2014-06-01 06:34:18 +00:00
2009-12-29 20:00:57 +00:00
buf [ 1 ] = b32_5to8 ( ( rand_seed > > 10 ) & 0x1f ) ;
buf [ 2 ] = b32_5to8 ( ( rand_seed > > 5 ) & 0x1f ) ;
buf [ 3 ] = b32_5to8 ( ( rand_seed ) & 0x1f ) ;
rand_seed + + ;
strncat ( buf , s , 512 ) ;
strncat ( buf , " . " , 512 ) ;
strncat ( buf , topdomain , 512 - strlen ( buf ) ) ;
2015-08-29 12:11:21 +00:00
send_query ( fd , ( uint8_t * ) buf ) ;
2009-12-29 20:00:57 +00:00
}
static void
send_downenctest ( int fd , char downenc , int variant , char * s , int slen )
/* Note: content/handling of s is not defined yet. */
{
char buf [ 512 ] = " y_____. " ;
buf [ 1 ] = tolower ( downenc ) ;
buf [ 2 ] = b32_5to8 ( variant ) ;
buf [ 3 ] = b32_5to8 ( ( rand_seed > > 10 ) & 0x1f ) ;
buf [ 4 ] = b32_5to8 ( ( rand_seed > > 5 ) & 0x1f ) ;
buf [ 5 ] = b32_5to8 ( ( rand_seed ) & 0x1f ) ;
rand_seed + + ;
2009-08-16 12:30:26 +00:00
strncat ( buf , topdomain , 512 - strlen ( buf ) ) ;
2015-08-29 12:11:21 +00:00
send_query ( fd , ( uint8_t * ) buf ) ;
2009-08-16 12:30:26 +00:00
}
static void
send_codec_switch ( int fd , int userid , int bits )
{
2009-09-20 15:11:14 +00:00
char buf [ 512 ] = " s_____. " ;
2009-08-16 12:30:26 +00:00
buf [ 1 ] = b32_5to8 ( userid ) ;
buf [ 2 ] = b32_5to8 ( bits ) ;
2014-06-01 06:34:18 +00:00
2009-08-16 12:30:26 +00:00
buf [ 3 ] = b32_5to8 ( ( rand_seed > > 10 ) & 0x1f ) ;
buf [ 4 ] = b32_5to8 ( ( rand_seed > > 5 ) & 0x1f ) ;
buf [ 5 ] = b32_5to8 ( ( rand_seed ) & 0x1f ) ;
rand_seed + + ;
strncat ( buf , topdomain , 512 - strlen ( buf ) ) ;
2015-08-29 12:11:21 +00:00
send_query ( fd , ( uint8_t * ) buf ) ;
2009-08-16 12:30:26 +00:00
}
2009-09-20 15:11:14 +00:00
2015-09-28 05:07:00 +00:00
static void
2015-09-28 12:06:23 +00:00
send_server_options ( int fd , int userid , int lazy , int compression , char denc , char * options )
/* Options must be length >=4 */
2015-09-28 05:07:00 +00:00
{
2015-09-28 12:06:23 +00:00
char buf [ 512 ] = " oU3___CMC. " ;
2015-09-28 05:07:00 +00:00
buf [ 1 ] = b32_5to8 ( userid ) ;
2015-09-28 12:06:23 +00:00
options [ 0 ] = tolower ( denc ) ;
options [ 1 ] = lazy ? ' l ' : ' i ' ;
options [ 2 ] = compression ? ' c ' : ' d ' ;
options [ 3 ] = 0 ;
strncpy ( buf + 3 , options , 3 ) ;
2009-09-20 15:11:14 +00:00
2015-09-28 12:06:23 +00:00
buf [ 6 ] = b32_5to8 ( ( rand_seed > > 10 ) & 0x1f ) ;
buf [ 7 ] = b32_5to8 ( ( rand_seed > > 5 ) & 0x1f ) ;
buf [ 8 ] = b32_5to8 ( ( rand_seed ) & 0x1f ) ;
2009-12-29 20:00:57 +00:00
rand_seed + + ;
2009-09-20 21:10:44 +00:00
strncat ( buf , topdomain , 512 - strlen ( buf ) ) ;
2015-08-29 12:11:21 +00:00
send_query ( fd , ( uint8_t * ) buf ) ;
2009-09-20 21:10:44 +00:00
}
2009-08-16 12:30:26 +00:00
static int
handshake_version ( int dns_fd , int * seed )
{
2009-09-20 21:10:44 +00:00
char hex [ ] = " 0123456789abcdef " ;
char hex2 [ ] = " 0123456789ABCDEF " ;
2009-08-16 12:30:26 +00:00
char in [ 4096 ] ;
uint32_t payload ;
int i ;
int read ;
for ( i = 0 ; running & & i < 5 ; i + + ) {
2014-04-24 14:03:47 +00:00
send_version ( dns_fd , PROTOCOL_VERSION ) ;
2009-08-16 12:30:26 +00:00
2015-09-28 05:07:00 +00:00
read = handshake_waitdns ( dns_fd , in , sizeof ( in ) , ' V ' , i + 1 ) ;
2009-08-16 12:30:26 +00:00
2012-02-06 19:28:42 +00:00
if ( read > = 9 ) {
payload = ( ( ( in [ 4 ] & 0xff ) < < 24 ) |
( ( in [ 5 ] & 0xff ) < < 16 ) |
( ( in [ 6 ] & 0xff ) < < 8 ) |
( ( in [ 7 ] & 0xff ) ) ) ;
2015-08-29 12:11:21 +00:00
if ( strncmp ( " VACK " , ( char * ) in , 4 ) = = 0 ) {
2012-02-06 19:28:42 +00:00
* seed = payload ;
userid = in [ 8 ] ;
userid_char = hex [ userid & 15 ] ;
userid_char2 = hex2 [ userid & 15 ] ;
2014-04-24 14:03:47 +00:00
fprintf ( stderr , " Version ok, both using protocol v 0x%08x. You are user #%d \n " ,
PROTOCOL_VERSION , userid ) ;
2012-02-06 19:28:42 +00:00
return 0 ;
2015-08-29 12:11:21 +00:00
} else if ( strncmp ( " VNAK " , ( char * ) in , 4 ) = = 0 ) {
2014-06-01 06:34:18 +00:00
warnx ( " You use protocol v 0x%08x, server uses v 0x%08x. Giving up " ,
2014-04-24 14:03:47 +00:00
PROTOCOL_VERSION , payload ) ;
2012-02-06 19:28:42 +00:00
return 1 ;
2015-08-29 12:11:21 +00:00
} else if ( strncmp ( " VFUL " , ( char * ) in , 4 ) = = 0 ) {
2012-02-06 19:28:42 +00:00
warnx ( " Server full, all %d slots are taken. Try again later " , payload ) ;
return 1 ;
}
} else if ( read > 0 )
warnx ( " did not receive proper login challenge " ) ;
2014-06-01 06:34:18 +00:00
2009-08-16 12:30:26 +00:00
fprintf ( stderr , " Retrying version check... \n " ) ;
}
2009-09-20 15:11:14 +00:00
warnx ( " couldn't connect to server (maybe other -T options will work) " ) ;
2009-08-16 12:30:26 +00:00
return 1 ;
}
static int
handshake_login ( int dns_fd , int seed )
{
char in [ 4096 ] ;
char login [ 16 ] ;
char server [ 65 ] ;
char client [ 65 ] ;
int mtu ;
int i ;
int read ;
login_calculate ( login , 16 , password , seed ) ;
2014-06-01 06:34:18 +00:00
2009-08-16 12:30:26 +00:00
for ( i = 0 ; running & & i < 5 ; i + + ) {
send_login ( dns_fd , login , 16 ) ;
2015-09-28 05:07:00 +00:00
read = handshake_waitdns ( dns_fd , in , sizeof ( in ) , ' L ' , i + 1 ) ;
2009-08-16 12:30:26 +00:00
2012-02-06 19:28:42 +00:00
if ( read > 0 ) {
int netmask ;
if ( strncmp ( " LNAK " , in , 4 ) = = 0 ) {
fprintf ( stderr , " Bad password \n " ) ;
return 1 ;
} else if ( sscanf ( in , " %64[^-]-%64[^-]-%d-%d " ,
server , client , & mtu , & netmask ) = = 4 ) {
server [ 64 ] = 0 ;
client [ 64 ] = 0 ;
if ( tun_setip ( client , server , netmask ) = = 0 & &
tun_setmtu ( mtu ) = = 0 ) {
fprintf ( stderr , " Server tunnel IP is %s \n " , server ) ;
return 0 ;
2009-08-16 12:30:26 +00:00
} else {
2012-02-06 19:28:42 +00:00
errx ( 4 , " Failed to set IP and MTU " ) ;
2009-08-16 12:30:26 +00:00
}
2012-02-06 19:28:42 +00:00
} else {
fprintf ( stderr , " Received bad handshake \n " ) ;
2009-08-16 12:30:26 +00:00
}
2012-02-06 19:28:42 +00:00
}
2009-08-16 12:30:26 +00:00
fprintf ( stderr , " Retrying login... \n " ) ;
}
warnx ( " couldn't login to server " ) ;
return 1 ;
}
static int
handshake_raw_udp ( int dns_fd , int seed )
{
struct timeval tv ;
char in [ 4096 ] ;
fd_set fds ;
int i ;
int r ;
int len ;
2015-06-28 20:41:54 +00:00
int got_addr ;
memset ( & raw_serv , 0 , sizeof ( raw_serv ) ) ;
got_addr = 0 ;
2009-08-16 12:30:26 +00:00
2009-12-29 20:00:57 +00:00
fprintf ( stderr , " Testing raw UDP data to the server (skip with -r) " ) ;
2009-08-16 12:30:26 +00:00
for ( i = 0 ; running & & i < 3 ; i + + ) {
send_ip_request ( dns_fd , userid ) ;
2015-09-28 05:07:00 +00:00
len = handshake_waitdns ( dns_fd , in , sizeof ( in ) , ' I ' , i + 1 ) ;
2009-08-16 12:30:26 +00:00
2012-02-06 19:28:42 +00:00
if ( len = = 5 & & in [ 0 ] = = ' I ' ) {
2015-06-28 20:41:54 +00:00
/* Received IPv4 address */
struct sockaddr_in * raw4_serv = ( struct sockaddr_in * ) & raw_serv ;
raw4_serv - > sin_family = AF_INET ;
memcpy ( & raw4_serv - > sin_addr , & in [ 1 ] , sizeof ( struct in_addr ) ) ;
raw4_serv - > sin_port = htons ( 53 ) ;
raw_serv_len = sizeof ( struct sockaddr_in ) ;
got_addr = 1 ;
break ;
}
if ( len = = 17 & & in [ 0 ] = = ' I ' ) {
/* Received IPv6 address */
struct sockaddr_in6 * raw6_serv = ( struct sockaddr_in6 * ) & raw_serv ;
raw6_serv - > sin6_family = AF_INET6 ;
memcpy ( & raw6_serv - > sin6_addr , & in [ 1 ] , sizeof ( struct in6_addr ) ) ;
raw6_serv - > sin6_port = htons ( 53 ) ;
raw_serv_len = sizeof ( struct sockaddr_in6 ) ;
got_addr = 1 ;
2012-02-06 19:28:42 +00:00
break ;
}
2009-12-29 20:00:57 +00:00
fprintf ( stderr , " . " ) ;
fflush ( stderr ) ;
2009-08-16 12:30:26 +00:00
}
2009-12-29 20:00:57 +00:00
fprintf ( stderr , " \n " ) ;
if ( ! running )
return 0 ;
2014-06-01 06:34:18 +00:00
2015-06-28 20:41:54 +00:00
if ( ! got_addr ) {
2009-08-16 12:30:26 +00:00
fprintf ( stderr , " Failed to get raw server IP, will use DNS mode. \n " ) ;
return 0 ;
}
2015-06-28 20:41:54 +00:00
fprintf ( stderr , " Server is at %s, trying raw login: " , format_addr ( & raw_serv , raw_serv_len ) ) ;
2009-08-16 12:30:26 +00:00
fflush ( stderr ) ;
2014-06-01 06:34:18 +00:00
/* do login against port 53 on remote server
2009-08-16 12:30:26 +00:00
* based on the old seed . If reply received ,
* switch to raw udp mode */
for ( i = 0 ; running & & i < 4 ; i + + ) {
tv . tv_sec = i + 1 ;
tv . tv_usec = 0 ;
send_raw_udp_login ( dns_fd , userid , seed ) ;
2014-06-01 06:34:18 +00:00
2009-08-16 12:30:26 +00:00
FD_ZERO ( & fds ) ;
FD_SET ( dns_fd , & fds ) ;
r = select ( dns_fd + 1 , & fds , NULL , NULL , & tv ) ;
if ( r > 0 ) {
/* recv() needed for windows, dont change to read() */
len = recv ( dns_fd , in , sizeof ( in ) , 0 ) ;
if ( len > = ( 16 + RAW_HDR_LEN ) ) {
char hash [ 16 ] ;
login_calculate ( hash , 16 , password , seed - 1 ) ;
if ( memcmp ( in , raw_header , RAW_HDR_IDENT_LEN ) = = 0
2014-06-01 06:34:18 +00:00
& & RAW_HDR_GET_CMD ( in ) = = RAW_HDR_CMD_LOGIN
2009-08-16 12:30:26 +00:00
& & memcmp ( & in [ RAW_HDR_LEN ] , hash , sizeof ( hash ) ) = = 0 ) {
fprintf ( stderr , " OK \n " ) ;
return 1 ;
}
}
}
fprintf ( stderr , " . " ) ;
fflush ( stderr ) ;
}
2014-06-01 06:34:18 +00:00
2009-08-16 12:30:26 +00:00
fprintf ( stderr , " failed \n " ) ;
return 0 ;
}
static int
2009-12-29 20:00:57 +00:00
handshake_upenctest ( int dns_fd , char * s )
/* NOTE: *s may be max 59 chars; must start with "aA" for case-swap check
Returns :
- 1 : case swap , no need for any further test : error printed ; or Ctrl - C
0 : not identical or error or timeout
1 : identical string returned
*/
2009-08-16 12:30:26 +00:00
{
char in [ 4096 ] ;
2009-12-29 20:00:57 +00:00
unsigned char * uin = ( unsigned char * ) in ;
unsigned char * us = ( unsigned char * ) s ;
2009-08-16 12:30:26 +00:00
int i ;
int read ;
2009-12-29 20:00:57 +00:00
int slen ;
2009-08-16 12:30:26 +00:00
2009-12-29 20:00:57 +00:00
slen = strlen ( s ) ;
for ( i = 0 ; running & & i < 3 ; i + + ) {
2009-08-16 12:30:26 +00:00
2009-12-29 20:00:57 +00:00
send_upenctest ( dns_fd , s ) ;
2009-08-16 12:30:26 +00:00
2015-09-28 05:07:00 +00:00
read = handshake_waitdns ( dns_fd , in , sizeof ( in ) , ' Z ' , i + 1 ) ;
2009-08-16 12:30:26 +00:00
2009-12-29 20:00:57 +00:00
if ( read = = - 2 )
return 0 ; /* hard error */
if ( read > 0 & & read < slen + 4 )
return 0 ; /* reply too short (chars dropped) */
if ( read > 0 ) {
int k ;
/* quick check if case swapped, to give informative error msg */
if ( in [ 4 ] = = ' A ' ) {
fprintf ( stderr , " DNS queries get changed to uppercase, keeping upstream codec Base32 \n " ) ;
return - 1 ;
}
if ( in [ 5 ] = = ' a ' ) {
fprintf ( stderr , " DNS queries get changed to lowercase, keeping upstream codec Base32 \n " ) ;
return - 1 ;
}
for ( k = 0 ; k < slen ; k + + ) {
if ( in [ k + 4 ] ! = s [ k ] ) {
/* Definitely not reliable */
if ( in [ k + 4 ] > = ' ' & & in [ k + 4 ] < = ' ~ ' & &
s [ k ] > = ' ' & & s [ k ] < = ' ~ ' ) {
fprintf ( stderr , " DNS query char '%c' gets changed into '%c' \n " ,
s [ k ] , in [ k + 4 ] ) ;
} else {
fprintf ( stderr , " DNS query char 0x%02X gets changed into 0x%02X \n " ,
( unsigned int ) us [ k ] ,
( unsigned int ) uin [ k + 4 ] ) ;
2009-08-16 12:30:26 +00:00
}
2009-12-29 20:00:57 +00:00
return 0 ;
}
}
/* if still here, then all okay */
return 1 ;
}
fprintf ( stderr , " Retrying upstream codec test... \n " ) ;
}
if ( ! running )
return - 1 ;
/* timeout */
return 0 ;
}
static int
handshake_upenc_autodetect ( int dns_fd )
/* Returns:
0 : keep Base32
1 : Base64 is okay
2 : Base64u is okay
3 : Base128 is okay
*/
{
/* Note: max 59 chars, must start with "aA".
pat64 : If 012 9 work , assume 3 - 8 are okay too .
RFC1035 par 2.3 .1 states that [ A - Z0 - 9 - ] allowed , but only
[ A - Z ] as first , and [ A - Z0 - 9 ] as last char _per label_ .
Test by having ' - ' as last char .
*/
char * pat64 = " aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ+0129- " ;
char * pat64u = " aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ_0129- " ;
char * pat128a = " aA-Aaahhh-Drink-mal-ein-J \344 germeister- " ;
char * pat128b = " aA-La-fl \373 te-na \357 ve-fran \347 aise-est-retir \351 - \340 -Cr \350 te " ;
char * pat128c = " aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ " ;
char * pat128d = " aA0123456789 \274 \275 \276 \277 "
" \300 \301 \302 \303 \304 \305 \306 \307 \310 \311 \312 \313 \314 \315 \316 \317 " ;
char * pat128e = " aA "
" \320 \321 \322 \323 \324 \325 \326 \327 \330 \331 \332 \333 \334 \335 \336 \337 "
" \340 \341 \342 \343 \344 \345 \346 \347 \350 \351 \352 \353 \354 \355 \356 \357 "
" \360 \361 \362 \363 \364 \365 \366 \367 \370 \371 \372 \373 \374 \375 " ;
int res ;
/* Try Base128, starting very gently to not draw attention */
while ( 1 ) {
res = handshake_upenctest ( dns_fd , pat128a ) ;
if ( res < 0 ) {
/* DNS swaps case, msg already printed; or Ctrl-C */
return 0 ;
} else if ( res = = 0 ) {
/* Probably not okay, skip Base128 entirely */
break ;
}
res = handshake_upenctest ( dns_fd , pat128b ) ;
if ( res < 0 )
return 0 ;
else if ( res = = 0 )
break ;
/* if this works, we can test the real stuff */
res = handshake_upenctest ( dns_fd , pat128c ) ;
if ( res < 0 )
return 0 ;
else if ( res = = 0 )
break ;
res = handshake_upenctest ( dns_fd , pat128d ) ;
if ( res < 0 )
return 0 ;
else if ( res = = 0 )
break ;
res = handshake_upenctest ( dns_fd , pat128e ) ;
if ( res < 0 )
return 0 ;
else if ( res = = 0 )
break ;
/* if still here, then base128 works completely */
return 3 ;
}
/* Try Base64 (with plus sign) */
res = handshake_upenctest ( dns_fd , pat64 ) ;
if ( res < 0 ) {
/* DNS swaps case, msg already printed; or Ctrl-C */
return 0 ;
} else if ( res > 0 ) {
/* All okay, Base64 msg will be printed later */
return 1 ;
}
/* Try Base64u (with _u_nderscore) */
res = handshake_upenctest ( dns_fd , pat64u ) ;
if ( res < 0 ) {
/* DNS swaps case, msg already printed; or Ctrl-C */
return 0 ;
} else if ( res > 0 ) {
/* All okay, Base64u msg will be printed later */
return 2 ;
}
/* if here, then nonthing worked */
fprintf ( stderr , " Keeping upstream codec Base32 \n " ) ;
return 0 ;
}
static int
handshake_downenctest ( int dns_fd , char trycodec )
/* Returns:
0 : not identical or error or timeout
1 : identical string returned
*/
{
char in [ 4096 ] ;
int i ;
int read ;
char * s = DOWNCODECCHECK1 ;
int slen = DOWNCODECCHECK1_LEN ;
for ( i = 0 ; running & & i < 3 ; i + + ) {
send_downenctest ( dns_fd , trycodec , 1 , NULL , 0 ) ;
2015-09-28 05:07:00 +00:00
read = handshake_waitdns ( dns_fd , in , sizeof ( in ) , ' Y ' , i + 1 ) ;
2009-12-29 20:00:57 +00:00
if ( read = = - 2 )
return 0 ; /* hard error */
if ( read > 0 & & read ! = slen )
return 0 ; /* reply incorrect = unreliable */
if ( read > 0 ) {
int k ;
for ( k = 0 ; k < slen ; k + + ) {
if ( in [ k ] ! = s [ k ] ) {
/* Definitely not reliable */
return 0 ;
2009-08-16 12:30:26 +00:00
}
}
2009-12-29 20:00:57 +00:00
/* if still here, then all okay */
return 1 ;
2009-08-16 12:30:26 +00:00
}
2009-12-29 20:00:57 +00:00
fprintf ( stderr , " Retrying downstream codec test... \n " ) ;
}
/* timeout */
return 0 ;
}
static char
handshake_downenc_autodetect ( int dns_fd )
/* Returns codec char (or ' ' if no advanced codec works) */
{
int base64ok = 0 ;
int base64uok = 0 ;
int base128ok = 0 ;
2014-06-09 18:05:29 +00:00
if ( do_qtype = = T_NULL | | do_qtype = = T_PRIVATE ) {
2009-12-29 20:00:57 +00:00
/* no other choice than raw */
fprintf ( stderr , " No alternative downstream codec available, using default (Raw) \n " ) ;
2015-09-28 05:07:00 +00:00
return ' R ' ;
2009-12-29 20:00:57 +00:00
}
fprintf ( stderr , " Autodetecting downstream codec (use -O to override) \n " ) ;
/* Try Base64 */
if ( handshake_downenctest ( dns_fd , ' S ' ) )
base64ok = 1 ;
else if ( running & & handshake_downenctest ( dns_fd , ' U ' ) )
base64uok = 1 ;
/* Try Base128 only if 64 gives us some perspective */
if ( running & & ( base64ok | | base64uok ) ) {
if ( handshake_downenctest ( dns_fd , ' V ' ) )
base128ok = 1 ;
2009-08-16 12:30:26 +00:00
}
2009-12-29 20:00:57 +00:00
/* If 128 works, then TXT may give us Raw as well */
if ( running & & ( base128ok & & do_qtype = = T_TXT ) ) {
if ( handshake_downenctest ( dns_fd , ' R ' ) )
return ' R ' ;
}
if ( ! running )
return ' ' ;
if ( base128ok )
return ' V ' ;
if ( base64ok )
return ' S ' ;
if ( base64uok )
return ' U ' ;
fprintf ( stderr , " No advanced downstream codecs seem to work, using default (Base32) \n " ) ;
return ' ' ;
}
static int
handshake_qtypetest ( int dns_fd , int timeout )
/* Returns:
0 : doesn ' t work with this timeout
1 : works properly
*/
{
char in [ 4096 ] ;
int read ;
char * s = DOWNCODECCHECK1 ;
int slen = DOWNCODECCHECK1_LEN ;
int trycodec ;
int k ;
2014-06-09 18:05:29 +00:00
if ( do_qtype = = T_NULL | | do_qtype = = T_PRIVATE )
2009-12-29 20:00:57 +00:00
trycodec = ' R ' ;
else
trycodec = ' T ' ;
/* We could use 'Z' bouncing here, but 'Y' also tests that 0-255
2014-06-09 18:05:29 +00:00
byte values can be returned , which is needed for NULL / PRIVATE
to work . */
2009-12-29 20:00:57 +00:00
send_downenctest ( dns_fd , trycodec , 1 , NULL , 0 ) ;
2015-09-28 05:07:00 +00:00
read = handshake_waitdns ( dns_fd , in , sizeof ( in ) , ' Y ' , timeout ) ;
2009-12-29 20:00:57 +00:00
if ( read ! = slen )
return 0 ; /* incorrect */
for ( k = 0 ; k < slen ; k + + ) {
if ( in [ k ] ! = s [ k ] ) {
/* corrupted */
return 0 ;
}
}
/* if still here, then all okay */
return 1 ;
}
static int
handshake_qtype_numcvt ( int num )
{
switch ( num ) {
case 0 : return T_NULL ;
2014-06-09 18:05:29 +00:00
case 1 : return T_PRIVATE ;
case 2 : return T_TXT ;
case 3 : return T_SRV ;
case 4 : return T_MX ;
case 5 : return T_CNAME ;
case 6 : return T_A ;
2009-12-29 20:00:57 +00:00
}
return T_UNSET ;
}
static int
handshake_qtype_autodetect ( int dns_fd )
/* Returns:
0 : okay , do_qtype set
1 : problem , program exit
*/
{
int highestworking = 100 ;
int timeout ;
int qtypenum ;
fprintf ( stderr , " Autodetecting DNS query type (use -T to override) " ) ;
fflush ( stderr ) ;
/* Method: try all "interesting" qtypes with a 1-sec timeout, then try
all " still-interesting " qtypes with a 2 - sec timeout , etc .
" Interesting " means : qtypes that ( are expected to ) have higher
bandwidth than what we know is working already ( highestworking ) .
Note that DNS relays may not immediately resolve the first ( NULL )
query in 1 sec , due to long recursive lookups , so we keep trying
to see if things will start working after a while .
*/
for ( timeout = 1 ; running & & timeout < = 3 ; timeout + + ) {
for ( qtypenum = 0 ; running & & qtypenum < highestworking ; qtypenum + + ) {
do_qtype = handshake_qtype_numcvt ( qtypenum ) ;
if ( do_qtype = = T_UNSET )
break ; /* this round finished */
fprintf ( stderr , " . " ) ;
fflush ( stderr ) ;
if ( handshake_qtypetest ( dns_fd , timeout ) ) {
/* okay */
highestworking = qtypenum ;
2015-10-04 14:39:40 +00:00
DEBUG ( 1 , " Type %s timeout %d works " , client_get_qtype ( ) , timeout ) ;
2009-12-29 20:00:57 +00:00
break ;
/* try others with longer timeout */
}
/* else: try next qtype with same timeout */
}
if ( highestworking = = 0 )
/* good, we have NULL; abort immediately */
break ;
}
fprintf ( stderr , " \n " ) ;
if ( ! running ) {
warnx ( " Stopped while autodetecting DNS query type (try setting manually with -T) " ) ;
return 1 ; /* problem */
}
/* finished */
do_qtype = handshake_qtype_numcvt ( highestworking ) ;
if ( do_qtype = = T_UNSET ) {
/* also catches highestworking still 100 */
warnx ( " No suitable DNS query type found. Are you connected to a network? " ) ;
warnx ( " If you expect very long roundtrip delays, use -T explicitly. " ) ;
warnx ( " (Also, connecting to an \" ancient \" version of iodined won't work.) " ) ;
return 1 ; /* problem */
}
/* "using qtype" message printed in handshake function */
return 0 ; /* okay */
}
static int
handshake_edns0_check ( int dns_fd )
/* Returns:
0 : EDNS0 not supported ; or Ctrl - C
1 : EDNS0 works
*/
{
char in [ 4096 ] ;
int i ;
int read ;
char * s = DOWNCODECCHECK1 ;
int slen = DOWNCODECCHECK1_LEN ;
char trycodec ;
if ( do_qtype = = T_NULL )
trycodec = ' R ' ;
else
trycodec = ' T ' ;
for ( i = 0 ; running & & i < 3 ; i + + ) {
send_downenctest ( dns_fd , trycodec , 1 , NULL , 0 ) ;
2015-09-28 05:07:00 +00:00
read = handshake_waitdns ( dns_fd , in , sizeof ( in ) , ' Y ' , i + 1 ) ;
2009-12-29 20:00:57 +00:00
if ( read = = - 2 )
return 0 ; /* hard error */
if ( read > 0 & & read ! = slen )
return 0 ; /* reply incorrect = unreliable */
if ( read > 0 ) {
int k ;
for ( k = 0 ; k < slen ; k + + ) {
if ( in [ k ] ! = s [ k ] ) {
/* Definitely not reliable */
return 0 ;
}
}
/* if still here, then all okay */
return 1 ;
}
fprintf ( stderr , " Retrying EDNS0 support test... \n " ) ;
}
/* timeout or Ctrl-C */
return 0 ;
2009-08-16 12:30:26 +00:00
}
static void
2009-12-29 20:00:57 +00:00
handshake_switch_codec ( int dns_fd , int bits )
2009-08-16 12:30:26 +00:00
{
char in [ 4096 ] ;
int i ;
int read ;
2009-12-29 20:00:57 +00:00
struct encoder * tempenc ;
2009-08-16 12:30:26 +00:00
2009-12-29 20:00:57 +00:00
if ( bits = = 5 )
tempenc = get_base32_encoder ( ) ;
else if ( bits = = 6 )
tempenc = get_base64_encoder ( ) ;
else if ( bits = = 26 ) /* "2nd" 6 bits per byte, with underscore */
tempenc = get_base64u_encoder ( ) ;
else if ( bits = = 7 )
tempenc = get_base128_encoder ( ) ;
else return ;
2009-08-16 12:30:26 +00:00
2009-12-29 20:00:57 +00:00
fprintf ( stderr , " Switching upstream to codec %s \n " , tempenc - > name ) ;
for ( i = 0 ; running & & i < 5 ; i + + ) {
2009-08-16 12:30:26 +00:00
send_codec_switch ( dns_fd , userid , bits ) ;
2014-06-01 06:34:18 +00:00
2015-09-28 05:07:00 +00:00
read = handshake_waitdns ( dns_fd , in , sizeof ( in ) , ' S ' , i + 1 ) ;
2009-08-16 12:30:26 +00:00
2012-02-06 19:28:42 +00:00
if ( read > 0 ) {
if ( strncmp ( " BADLEN " , in , 6 ) = = 0 ) {
2015-08-29 12:11:21 +00:00
fprintf ( stderr , " Server got bad message length. \n " ) ;
2012-02-06 19:28:42 +00:00
goto codec_revert ;
} else if ( strncmp ( " BADIP " , in , 5 ) = = 0 ) {
2015-08-29 12:11:21 +00:00
fprintf ( stderr , " Server rejected sender IP address. \n " ) ;
2012-02-06 19:28:42 +00:00
goto codec_revert ;
} else if ( strncmp ( " BADCODEC " , in , 8 ) = = 0 ) {
2015-08-29 12:11:21 +00:00
fprintf ( stderr , " Server rejected the selected codec. \n " ) ;
2012-02-06 19:28:42 +00:00
goto codec_revert ;
2009-08-16 12:30:26 +00:00
}
2012-02-06 19:28:42 +00:00
in [ read ] = 0 ; /* zero terminate */
fprintf ( stderr , " Server switched upstream to codec %s \n " , in ) ;
dataenc = tempenc ;
2015-08-23 14:14:27 +00:00
/* Update outgoing buffer max (decoded) fragsize */
2015-09-28 05:07:00 +00:00
maxfragsize_up = get_raw_length_from_dns ( hostname_maxlen - UPSTREAM_HDR , dataenc , topdomain ) ;
2012-02-06 19:28:42 +00:00
return ;
}
2009-12-29 20:00:57 +00:00
2009-08-16 12:30:26 +00:00
fprintf ( stderr , " Retrying codec switch... \n " ) ;
}
2009-12-29 20:00:57 +00:00
if ( ! running )
return ;
2015-08-29 12:11:21 +00:00
fprintf ( stderr , " No reply from server on codec switch. \n " ) ;
2009-08-16 12:30:26 +00:00
2014-06-01 06:34:18 +00:00
codec_revert :
2009-12-29 20:00:57 +00:00
fprintf ( stderr , " Falling back to upstream codec %s \n " , dataenc - > name ) ;
2009-08-16 12:30:26 +00:00
}
2015-09-28 12:06:23 +00:00
void
handshake_switch_options ( int dns_fd , int lazy , int compression , char denc )
2015-09-28 05:07:00 +00:00
{
char in [ 4096 ] ;
int read ;
2015-09-28 12:06:23 +00:00
char * dname , * comp_status , * lazy_status ;
char opts [ 4 ] ;
2015-09-28 05:07:00 +00:00
2015-09-28 12:06:23 +00:00
comp_status = compression ? " enabled " : " disabled " ;
2009-09-20 15:11:14 +00:00
dname = " Base32 " ;
2015-09-28 12:06:23 +00:00
if ( denc = = ' S ' )
2009-09-20 15:11:14 +00:00
dname = " Base64 " ;
2015-09-28 12:06:23 +00:00
else if ( denc = = ' U ' )
2009-12-29 20:00:57 +00:00
dname = " Base64u " ;
2015-09-28 12:06:23 +00:00
else if ( denc = = ' V ' )
2009-12-29 20:00:57 +00:00
dname = " Base128 " ;
2015-09-28 12:06:23 +00:00
else if ( denc = = ' R ' )
2009-09-20 15:11:14 +00:00
dname = " Raw " ;
2015-09-28 12:06:23 +00:00
lazy_status = lazy ? " lazy " : " immediate " ;
2009-09-20 15:11:14 +00:00
2015-09-28 12:06:23 +00:00
fprintf ( stderr , " Switching server options: %s mode, downstream codec %s, compression %s... \n " ,
lazy_status , dname , comp_status ) ;
for ( int i = 0 ; running & & i < 5 ; i + + ) {
2009-09-20 15:11:14 +00:00
2015-09-28 12:06:23 +00:00
send_server_options ( dns_fd , userid , lazy , compression , denc , opts ) ;
read = handshake_waitdns ( dns_fd , in , sizeof ( in ) - 1 , ' O ' , i + 1 ) ;
2009-09-20 15:11:14 +00:00
2012-02-06 19:28:42 +00:00
if ( read > 0 ) {
if ( strncmp ( " BADLEN " , in , 6 ) = = 0 ) {
2015-08-29 12:11:21 +00:00
fprintf ( stderr , " Server got bad message length. \n " ) ;
2015-09-28 12:06:23 +00:00
goto opt_revert ;
2012-02-06 19:28:42 +00:00
} else if ( strncmp ( " BADIP " , in , 5 ) = = 0 ) {
2015-08-29 12:11:21 +00:00
fprintf ( stderr , " Server rejected sender IP address. \n " ) ;
2015-09-28 12:06:23 +00:00
goto opt_revert ;
2012-02-06 19:28:42 +00:00
} else if ( strncmp ( " BADCODEC " , in , 8 ) = = 0 ) {
2015-09-28 12:06:23 +00:00
fprintf ( stderr , " Server rejected the selected options. \n " ) ;
goto opt_revert ;
2009-09-20 15:11:14 +00:00
}
2015-09-28 12:06:23 +00:00
fprintf ( stderr , " Switched server options successfully. (%s) \n " , opts ) ;
lazymode = lazy ;
compression_down = compression ;
downenc = denc ;
2012-02-06 19:28:42 +00:00
return ;
}
2009-12-29 20:00:57 +00:00
2009-09-20 15:11:14 +00:00
fprintf ( stderr , " Retrying codec switch... \n " ) ;
}
2009-12-29 20:00:57 +00:00
if ( ! running )
return ;
2015-08-29 12:11:21 +00:00
fprintf ( stderr , " No reply from server on codec switch. \n " ) ;
2009-09-20 15:11:14 +00:00
2015-09-28 12:06:23 +00:00
opt_revert :
2015-10-03 14:09:34 +00:00
comp_status = compression_down ? " enabled " : " disabled " ;
lazy_status = lazymode ? " lazy " : " immediate " ;
fprintf ( stderr , " Falling back to previous configuration: downstream codec %s, %s mode, compression %s. \n " ,
dataenc - > name , lazy_status , comp_status ) ;
2009-09-20 21:10:44 +00:00
}
2009-09-20 15:11:14 +00:00
static int
fragsize_check ( char * in , int read , int proposed_fragsize , int * max_fragsize )
/* Returns: 0: keep checking, 1: break loop (either okay or definitely wrong) */
{
int acked_fragsize = ( ( in [ 0 ] & 0xff ) < < 8 ) | ( in [ 1 ] & 0xff ) ;
2009-12-29 20:00:57 +00:00
int okay ;
int i ;
unsigned int v ;
2009-09-20 15:11:14 +00:00
if ( read > = 5 & & strncmp ( " BADIP " , in , 5 ) = = 0 ) {
fprintf ( stderr , " got BADIP (Try iodined -c).. \n " ) ;
fflush ( stderr ) ;
return 0 ; /* maybe temporary error */
}
if ( acked_fragsize ! = proposed_fragsize ) {
/*
* got ack for wrong fragsize , maybe late response for
* earlier query , or ack corrupted
*/
return 0 ;
}
if ( read ! = proposed_fragsize ) {
/*
* correctly acked fragsize but read too little ( or too
* much ) : this fragsize is definitely not reliable
*/
return 1 ;
}
/* here: read == proposed_fragsize == acked_fragsize */
/* test: */
/* in[123] = 123; */
2009-12-29 20:00:57 +00:00
if ( ( in [ 2 ] & 0xff ) ! = 107 ) {
2015-08-29 12:11:21 +00:00
warnx ( " \n corruption at byte 2, this won't work. Try -O Base32, or other -T options. " ) ;
2009-12-29 20:00:57 +00:00
* max_fragsize = - 1 ;
return 1 ;
}
2009-09-20 15:11:14 +00:00
/* Check for corruption */
2009-12-29 20:00:57 +00:00
okay = 1 ;
v = in [ 3 ] & 0xff ;
2009-09-20 15:11:14 +00:00
2012-02-06 19:28:42 +00:00
for ( i = 3 ; i < read ; i + + , v = ( v + 107 ) & 0xff )
if ( ( in [ i ] & 0xff ) ! = v ) {
okay = 0 ;
break ;
}
2009-09-20 15:11:14 +00:00
2012-02-06 19:28:42 +00:00
if ( okay ) {
fprintf ( stderr , " %d ok.. " , acked_fragsize ) ;
fflush ( stderr ) ;
* max_fragsize = acked_fragsize ;
return 1 ;
} else {
if ( downenc ! = ' ' & & downenc ! = ' T ' ) {
fprintf ( stderr , " %d corrupted at %d.. (Try -O Base32) \n " , acked_fragsize , i ) ;
2009-09-20 15:11:14 +00:00
} else {
2012-02-06 19:28:42 +00:00
fprintf ( stderr , " %d corrupted at %d.. " , acked_fragsize , i ) ;
2009-09-20 15:11:14 +00:00
}
2012-02-06 19:28:42 +00:00
fflush ( stderr ) ;
return 1 ;
}
2009-09-20 15:11:14 +00:00
2009-12-29 20:00:57 +00:00
/* notreached */
2009-09-20 15:11:14 +00:00
return 1 ;
}
2009-08-16 12:30:26 +00:00
static int
handshake_autoprobe_fragsize ( int dns_fd )
{
2015-08-29 12:11:21 +00:00
char in [ MAX_FRAGSIZE ] ;
2009-08-16 12:30:26 +00:00
int i ;
int read ;
int proposed_fragsize = 768 ;
int range = 768 ;
2009-09-20 15:11:14 +00:00
int max_fragsize ;
2009-08-16 12:30:26 +00:00
max_fragsize = 0 ;
2015-08-29 12:11:21 +00:00
fprintf ( stderr , " Autoprobing max downstream fragment size... (skip with -m fragsize) " ) ;
2009-09-20 15:11:14 +00:00
while ( running & & range > 0 & & ( range > = 8 | | max_fragsize < 300 ) ) {
/* stop the slow probing early when we have enough bytes anyway */
2009-08-16 12:30:26 +00:00
for ( i = 0 ; running & & i < 3 ; i + + ) {
2009-12-29 20:00:57 +00:00
send_fragsize_probe ( dns_fd , proposed_fragsize ) ;
2009-08-16 12:30:26 +00:00
2015-09-28 05:07:00 +00:00
read = handshake_waitdns ( dns_fd , in , sizeof ( in ) , ' R ' , 1 ) ;
2014-06-01 06:34:18 +00:00
2012-02-06 19:28:42 +00:00
if ( read > 0 ) {
/* We got a reply */
if ( fragsize_check ( in , read , proposed_fragsize , & max_fragsize ) = = 1 )
break ;
}
2009-12-29 20:00:57 +00:00
2009-08-16 12:30:26 +00:00
fprintf ( stderr , " . " ) ;
fflush ( stderr ) ;
}
2009-12-29 20:00:57 +00:00
if ( max_fragsize < 0 )
break ;
2009-08-16 12:30:26 +00:00
range > > = 1 ;
if ( max_fragsize = = proposed_fragsize ) {
/* Try bigger */
proposed_fragsize + = range ;
} else {
/* Try smaller */
fprintf ( stderr , " %d not ok.. " , proposed_fragsize ) ;
fflush ( stderr ) ;
proposed_fragsize - = range ;
}
}
if ( ! running ) {
2015-08-29 12:11:21 +00:00
warnx ( " \n stopped while autodetecting fragment size (Try setting manually with -m) " ) ;
2009-08-16 12:30:26 +00:00
return 0 ;
}
2015-08-21 03:08:47 +00:00
if ( max_fragsize < = 6 ) {
2009-12-29 20:00:57 +00:00
/* Tried all the way down to 2 and found no good size.
But we _did_ do all handshake before this , so there must
be some workable connection . */
2015-08-29 12:11:21 +00:00
warnx ( " \n found no accepted fragment size. " ) ;
2009-12-29 20:00:57 +00:00
warnx ( " try setting -M to 200 or lower, or try other -T or -O options. " ) ;
2009-08-16 12:30:26 +00:00
return 0 ;
}
2015-08-21 03:08:47 +00:00
/* data header adds 6 bytes */
fprintf ( stderr , " will use %d-6=%d \n " , max_fragsize , max_fragsize - 6 ) ;
2009-09-20 15:11:14 +00:00
2009-12-29 20:00:57 +00:00
/* need 1200 / 16frags = 75 bytes fragsize */
if ( max_fragsize < 82 ) {
fprintf ( stderr , " Note: this probably won't work well. \n " ) ;
fprintf ( stderr , " Try setting -M to 200 or lower, or try other DNS types (-T option). \n " ) ;
} else if ( max_fragsize < 202 & &
2014-06-09 18:05:29 +00:00
( do_qtype = = T_NULL | | do_qtype = = T_PRIVATE | | do_qtype = = T_TXT | |
2009-12-29 20:00:57 +00:00
do_qtype = = T_SRV | | do_qtype = = T_MX ) ) {
fprintf ( stderr , " Note: this isn't very much. \n " ) ;
fprintf ( stderr , " Try setting -M to 200 or lower, or try other DNS types (-T option). \n " ) ;
}
2009-09-20 15:11:14 +00:00
return max_fragsize - 2 ;
2009-08-16 12:30:26 +00:00
}
static void
handshake_set_fragsize ( int dns_fd , int fragsize )
{
char in [ 4096 ] ;
int i ;
int read ;
fprintf ( stderr , " Setting downstream fragment size to max %d... \n " , fragsize ) ;
for ( i = 0 ; running & & i < 5 ; i + + ) {
send_set_downstream_fragsize ( dns_fd , fragsize ) ;
2015-09-28 05:07:00 +00:00
read = handshake_waitdns ( dns_fd , in , sizeof ( in ) , ' N ' , i + 1 ) ;
2009-08-16 12:30:26 +00:00
2012-02-06 19:28:42 +00:00
if ( read > 0 ) {
2009-08-16 12:30:26 +00:00
2012-02-06 19:28:42 +00:00
if ( strncmp ( " BADFRAG " , in , 7 ) = = 0 ) {
2015-08-29 12:11:21 +00:00
fprintf ( stderr , " Server rejected fragsize. Keeping default. \n " ) ;
2012-02-06 19:28:42 +00:00
return ;
} else if ( strncmp ( " BADIP " , in , 5 ) = = 0 ) {
fprintf ( stderr , " Server rejected sender IP address. \n " ) ;
2009-08-16 12:30:26 +00:00
return ;
}
2012-02-06 19:28:42 +00:00
2014-01-29 18:33:54 +00:00
/* The server returns the accepted fragsize:
accepted_fragsize = ( ( in [ 0 ] & 0xff ) < < 8 ) | ( in [ 1 ] & 0xff ) */
2012-02-06 19:28:42 +00:00
return ;
}
2009-12-29 20:00:57 +00:00
2009-08-16 12:30:26 +00:00
fprintf ( stderr , " Retrying set fragsize... \n " ) ;
}
2009-12-29 20:00:57 +00:00
if ( ! running )
return ;
2009-08-16 12:30:26 +00:00
fprintf ( stderr , " No reply from server when setting fragsize. Keeping default. \n " ) ;
}
int
client_handshake ( int dns_fd , int raw_mode , int autodetect_frag_size , int fragsize )
{
int seed ;
2009-12-29 20:00:57 +00:00
int upcodec ;
2009-08-16 12:30:26 +00:00
int r ;
2009-12-29 20:00:57 +00:00
dnsc_use_edns0 = 0 ;
/* qtype message printed in handshake function */
if ( do_qtype = = T_UNSET ) {
r = handshake_qtype_autodetect ( dns_fd ) ;
if ( r ) {
return r ;
}
}
2014-06-09 16:11:16 +00:00
fprintf ( stderr , " Using DNS type %s queries \n " , client_get_qtype ( ) ) ;
2009-12-29 20:00:57 +00:00
2009-08-16 12:30:26 +00:00
r = handshake_version ( dns_fd , & seed ) ;
if ( r ) {
return r ;
}
r = handshake_login ( dns_fd , seed ) ;
if ( r ) {
return r ;
}
if ( raw_mode & & handshake_raw_udp ( dns_fd , seed ) ) {
2015-09-28 05:07:00 +00:00
/* TODO: upstream fragsize based on max raw packet size */
2009-08-16 12:30:26 +00:00
conn = CONN_RAW_UDP ;
2015-09-28 05:07:00 +00:00
max_timeout_ms = 10000 ;
2009-08-16 12:30:26 +00:00
} else {
if ( raw_mode = = 0 ) {
fprintf ( stderr , " Skipping raw mode \n " ) ;
}
2009-12-29 20:00:57 +00:00
dnsc_use_edns0 = 1 ;
if ( handshake_edns0_check ( dns_fd ) & & running ) {
fprintf ( stderr , " Using EDNS0 extension \n " ) ;
} else if ( ! running ) {
return - 1 ;
} else {
fprintf ( stderr , " DNS relay does not support EDNS0 extension \n " ) ;
dnsc_use_edns0 = 0 ;
}
upcodec = handshake_upenc_autodetect ( dns_fd ) ;
if ( ! running )
return - 1 ;
2015-08-23 14:14:27 +00:00
if ( upcodec = = 1 ) { /* Base64 */
2009-12-29 20:00:57 +00:00
handshake_switch_codec ( dns_fd , 6 ) ;
2015-08-23 14:14:27 +00:00
} else if ( upcodec = = 2 ) { /* Base64u */
2009-12-29 20:00:57 +00:00
handshake_switch_codec ( dns_fd , 26 ) ;
2015-08-23 14:14:27 +00:00
} else if ( upcodec = = 3 ) { /* Base128 */
2009-12-29 20:00:57 +00:00
handshake_switch_codec ( dns_fd , 7 ) ;
}
if ( ! running )
return - 1 ;
if ( downenc = = ' ' ) {
downenc = handshake_downenc_autodetect ( dns_fd ) ;
2009-08-16 12:30:26 +00:00
}
2009-12-29 20:00:57 +00:00
if ( ! running )
return - 1 ;
2009-08-16 12:30:26 +00:00
2015-09-28 12:06:23 +00:00
/* Set options for compression, lazymode and downstream codec */
handshake_switch_options ( dns_fd , lazymode , compression_down , downenc ) ;
2009-12-29 20:00:57 +00:00
if ( ! running )
return - 1 ;
2009-09-20 21:10:44 +00:00
2009-08-16 12:30:26 +00:00
if ( autodetect_frag_size ) {
fragsize = handshake_autoprobe_fragsize ( dns_fd ) ;
2015-08-21 03:08:47 +00:00
if ( fragsize > MAX_FRAGSIZE ) {
/* This is very unlikely except perhaps over LAN */
2015-08-21 08:57:54 +00:00
fprintf ( stderr , " Can transfer fragsize of %d, however iodine has been compiled with MAX_FRAGSIZE = %d. To fully utilize this connection, please recompile iodine/iodined. " , fragsize , MAX_FRAGSIZE ) ;
2015-08-21 03:08:47 +00:00
fragsize = MAX_FRAGSIZE ;
}
2009-08-16 12:30:26 +00:00
if ( ! fragsize ) {
return 1 ;
}
}
handshake_set_fragsize ( dns_fd , fragsize ) ;
2009-12-29 20:00:57 +00:00
if ( ! running )
return - 1 ;
2009-08-16 12:30:26 +00:00
}
return 0 ;
}