Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
D
Dnsmasq
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nanahira
Dnsmasq
Commits
8d71fdcd
Commit
8d71fdcd
authored
Feb 08, 2015
by
Chen Wei
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
DNS over tcp is working
parent
b9eb9201
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
223 additions
and
174 deletions
+223
-174
src/forward.c
src/forward.c
+223
-174
No files found.
src/forward.c
View file @
8d71fdcd
...
@@ -30,7 +30,9 @@ static int do_check_sign(struct frec *forward, int status, time_t now, char *nam
...
@@ -30,7 +30,9 @@ static int do_check_sign(struct frec *forward, int status, time_t now, char *nam
static
int
send_check_sign
(
struct
frec
*
forward
,
time_t
now
,
struct
dns_header
*
header
,
size_t
plen
,
static
int
send_check_sign
(
struct
frec
*
forward
,
time_t
now
,
struct
dns_header
*
header
,
size_t
plen
,
char
*
name
,
char
*
keyname
);
char
*
name
,
char
*
keyname
);
#endif
#endif
static
int
tcp_conn_serv
(
struct
server
*
serv
,
time_t
now
,
unsigned
char
*
packet
,
size_t
payload_size
,
union
mysockaddr
*
peer_addr
);
/* Send a UDP packet with its source address set as "source"
/* Send a UDP packet with its source address set as "source"
unless nowild is true, when we just send it with the kernel default */
unless nowild is true, when we just send it with the kernel default */
...
@@ -461,7 +463,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
...
@@ -461,7 +463,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#endif
#endif
}
}
//TODO
retry
//TODO
how to retry here?
if
(
sendto
(
fd
,
(
char
*
)
header
,
plen
,
0
,
if
(
sendto
(
fd
,
(
char
*
)
header
,
plen
,
0
,
&
fwdserv
->
addr
.
sa
,
sa_len
(
&
fwdserv
->
addr
))
==
-
1
)
&
fwdserv
->
addr
.
sa
,
sa_len
(
&
fwdserv
->
addr
))
==
-
1
)
{
{
...
@@ -1635,6 +1637,167 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
...
@@ -1635,6 +1637,167 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
}
}
#endif
#endif
/* try establish tcp connection to upstream server and forward query
*
* return -1 on tcp connect/read/write error
* 0 on upstream response has 0 length DNS message, or DNSSEC error
* > 0 DNS message length received from upstream server */
static
int
tcp_conn_serv
(
struct
server
*
serv
,
time_t
now
,
unsigned
char
*
packet
,
size_t
payload_size
,
union
mysockaddr
*
peer_addr
)
{
unsigned
char
*
payload
=
packet
+
2
;
/* skip msg length field */
struct
dns_header
*
header
=
(
struct
dns_header
*
)
payload
;
unsigned
int
gotname
;
unsigned
short
qtype
;
int
checking_disabled
=
header
->
hb4
&
HB4_CD
;
unsigned
int
m
=
0
;
u16
msg_len
;
#ifdef HAVE_DNSSEC
unsigned
char
*
newhash
,
hash
[
HASH_SIZE
];
if
((
newhash
=
hash_questions
(
header
,
(
unsigned
int
)
payload_size
,
daemon
->
namebuff
)))
memcpy
(
hash
,
newhash
,
HASH_SIZE
);
else
memset
(
hash
,
0
,
HASH_SIZE
);
#else
unsigned
int
crc
=
questions_crc
(
header
,
(
unsigned
int
)
payload_size
,
daemon
->
namebuff
);
#endif
if
(
serv
->
tcpfd
==
-
1
)
{
if
((
serv
->
tcpfd
=
socket
(
serv
->
addr
.
sa
.
sa_family
,
SOCK_STREAM
,
0
))
==
-
1
)
return
-
1
;
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if
(
option_bool
(
OPT_CONNTRACK
))
{
unsigned
int
mark
;
struct
all_addr
local
;
#ifdef HAVE_IPV6
if
(
local_addr
->
sa
.
sa_family
==
AF_INET6
)
local
.
addr
.
addr6
=
local_addr
->
in6
.
sin6_addr
;
else
#endif
local
.
addr
.
addr4
=
local_addr
->
in
.
sin_addr
;
if
(
get_incoming_mark
(
&
peer_addr
,
&
local
,
1
,
&
mark
))
setsockopt
(
serv
->
tcpfd
,
SOL_SOCKET
,
SO_MARK
,
&
mark
,
sizeof
(
unsigned
int
));
}
#endif
if
(
!
local_bind
(
serv
->
tcpfd
,
&
serv
->
source_addr
,
serv
->
interface
,
1
)
||
connect
(
serv
->
tcpfd
,
&
serv
->
addr
.
sa
,
sa_len
(
&
serv
->
addr
))
==
-
1
)
{
close
(
serv
->
tcpfd
);
serv
->
tcpfd
=
-
1
;
return
-
1
;
}
#ifdef HAVE_DNSSEC
if
(
option_bool
(
OPT_DNSSEC_VALID
))
{
size_t
new_size
=
add_do_bit
(
header
,
payload_size
,
((
char
*
)
header
)
+
65536
);
/* For debugging, set Checking Disabled, otherwise, have the upstream
check too, this allows it to select auth servers when one is
returning bad data. */
if
(
option_bool
(
OPT_DNSSEC_DEBUG
))
header
->
hb4
|=
HB4_CD
;
if
(
payload_size
!=
new_size
)
added_pheader
=
1
;
payload_size
=
new_size
;
}
#endif
}
/* get query name again for logging - may have been overwritten */
if
(
!
(
gotname
=
extract_request
(
header
,
(
unsigned
int
)
payload_size
,
daemon
->
namebuff
,
&
qtype
)))
strcpy
(
daemon
->
namebuff
,
"query"
);
u16
n_size
=
htons
(
payload_size
);
memcpy
(
packet
,
&
n_size
,
sizeof
(
u16
));
if
(
!
read_write
(
serv
->
tcpfd
,
packet
,
payload_size
+
sizeof
(
u16
),
0
)
||
!
read_write
(
serv
->
tcpfd
,
(
unsigned
char
*
)
&
msg_len
,
2
,
1
)
||
!
(
m
=
(
unsigned
int
)
ntohs
(
msg_len
))
||
!
read_write
(
serv
->
tcpfd
,
payload
,
(
int
)
m
,
1
))
{
close
(
serv
->
tcpfd
);
serv
->
tcpfd
=
-
1
;
return
-
1
;
}
if
(
serv
->
addr
.
sa
.
sa_family
==
AF_INET
)
log_query
(
F_SERVER
|
F_IPV4
|
F_FORWARD
,
daemon
->
namebuff
,
(
struct
all_addr
*
)
&
serv
->
addr
.
in
.
sin_addr
,
NULL
);
#ifdef HAVE_IPV6
else
log_query
(
F_SERVER
|
F_IPV6
|
F_FORWARD
,
daemon
->
namebuff
,
(
struct
all_addr
*
)
&
serv
->
addr
.
in6
.
sin6_addr
,
NULL
);
#endif
#ifdef HAVE_DNSSEC
if
(
option_bool
(
OPT_DNSSEC_VALID
)
&&
!
checking_disabled
)
{
/* Limit to number of DNSSEC questions, to catch loops and avoid filling
* cache. */
int
keycount
=
DNSSEC_WORK
;
int
status
=
tcp_key_recurse
(
now
,
STAT_TRUNCATED
,
header
,
m
,
0
,
daemon
->
namebuff
,
daemon
->
keyname
,
serv
,
&
keycount
);
char
*
result
;
if
(
keycount
==
0
)
result
=
"ABANDONED"
;
else
result
=
(
status
==
STAT_SECURE
?
"SECURE"
:
(
status
==
STAT_INSECURE
?
"INSECURE"
:
"BOGUS"
));
log_query
(
F_KEYTAG
|
F_SECSTAT
,
"result"
,
NULL
,
result
);
if
(
status
==
STAT_BOGUS
)
no_cache_dnssec
=
1
;
if
(
status
==
STAT_SECURE
)
cache_secure
=
1
;
}
#endif
/* restore CD bit to the value in the query */
if
(
checking_disabled
)
header
->
hb4
|=
HB4_CD
;
else
header
->
hb4
&=
~
HB4_CD
;
/* There's no point in updating the cache, since this process will exit and
lose the information after a few queries. We make this call for the alias and
bogus-nxdomain side-effects. */
/* If the crc of the question section doesn't match the crc we sent, then
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
#ifdef HAVE_DNSSEC
newhash
=
hash_questions
(
header
,
m
,
daemon
->
namebuff
);
if
(
!
newhash
||
memcmp
(
hash
,
newhash
,
HASH_SIZE
)
!=
0
)
m
=
0
;
#else
if
(
crc
!=
questions_crc
(
header
,
m
,
daemon
->
namebuff
))
m
=
0
;
#endif
return
(
int
)
m
;
}
/* The daemon forks before calling this: it should deal with one connection,
/* The daemon forks before calling this: it should deal with one connection,
blocking as neccessary, and then return. Note, need to be a bit careful
blocking as neccessary, and then return. Note, need to be a bit careful
...
@@ -1660,11 +1823,12 @@ unsigned char *tcp_request(int confd, time_t now,
...
@@ -1660,11 +1823,12 @@ unsigned char *tcp_request(int confd, time_t now,
/* largest field in header is 16-bits, so this is still sufficiently aligned */
/* largest field in header is 16-bits, so this is still sufficiently aligned */
struct
dns_header
*
header
=
(
struct
dns_header
*
)
payload
;
struct
dns_header
*
header
=
(
struct
dns_header
*
)
payload
;
u16
*
length
=
(
u16
*
)
packet
;
u16
*
length
=
(
u16
*
)
packet
;
struct
server
*
last_server
,
*
fwdserv
;
struct
server
*
last_server
,
*
fwdserv
,
*
serv
;
struct
in_addr
dst_addr_4
;
struct
in_addr
dst_addr_4
;
union
mysockaddr
peer_addr
;
union
mysockaddr
peer_addr
;
socklen_t
peer_len
=
sizeof
(
union
mysockaddr
);
socklen_t
peer_len
=
sizeof
(
union
mysockaddr
);
int
query_count
=
0
;
int
query_count
=
0
;
int
ret
;
if
(
getpeername
(
confd
,
(
struct
sockaddr
*
)
&
peer_addr
,
&
peer_len
)
==
-
1
)
if
(
getpeername
(
confd
,
(
struct
sockaddr
*
)
&
peer_addr
,
&
peer_len
)
==
-
1
)
return
packet
;
return
packet
;
...
@@ -1792,183 +1956,68 @@ unsigned char *tcp_request(int confd, time_t now,
...
@@ -1792,183 +1956,68 @@ unsigned char *tcp_request(int confd, time_t now,
}
}
}
}
//TODO use fwdserv
if
(
gotname
)
if
(
gotname
)
flags
=
search_servers
(
now
,
&
addrp
,
gotname
,
daemon
->
namebuff
,
&
type
,
&
domain
,
&
norebind
,
&
fwdserv
);
flags
=
search_servers
(
now
,
&
addrp
,
gotname
,
daemon
->
namebuff
,
&
type
,
&
domain
,
&
norebind
,
&
fwdserv
);
if
(
type
!=
0
||
option_bool
(
OPT_ORDER
)
||
!
daemon
->
last_server
)
ret
=
0
;
last_server
=
daemon
->
servers
;
serv
=
last_server
=
NULL
;
else
/* --address=/xxx/1.2.3.4 */
last_server
=
daemon
->
last_server
;
if
(
addrp
!=
NULL
)
{
if
(
!
flags
&&
last_server
)
m
=
0
;
{
}
struct
server
*
firstsendto
=
NULL
;
else
if
(
fwdserv
!=
NULL
)
#ifdef HAVE_DNSSEC
{
unsigned
char
*
newhash
,
hash
[
HASH_SIZE
];
/* --server=/example.org/1.2.3.4 */
if
((
newhash
=
hash_questions
(
header
,
(
unsigned
int
)
size
,
daemon
->
namebuff
)))
serv
=
fwdserv
;
memcpy
(
hash
,
newhash
,
HASH_SIZE
);
ret
=
tcp_conn_serv
(
serv
,
now
,
packet
,
size
,
&
peer_addr
);
else
}
memset
(
hash
,
0
,
HASH_SIZE
);
else
#else
{
unsigned
int
crc
=
questions_crc
(
header
,
(
unsigned
int
)
size
,
daemon
->
namebuff
);
/* use normal server */
#endif
struct
server
*
firstsendto
=
NULL
;
/* Loop round available servers until we succeed in connecting to one.
if
(
option_bool
(
OPT_ORDER
)
||
!
daemon
->
last_server
)
Note that this code subtley ensures that consecutive queries on this connection
last_server
=
daemon
->
servers
;
which can go to the same server, do so. */
else
while
(
1
)
last_server
=
daemon
->
last_server
;
{
if
(
!
firstsendto
)
while
(
1
)
firstsendto
=
last_server
;
{
else
if
(
!
firstsendto
)
{
firstsendto
=
last_server
;
if
(
!
(
last_server
=
last_server
->
next
))
else
last_server
=
daemon
->
servers
;
{
if
(
!
(
last_server
=
last_server
->
next
))
if
(
last_server
==
firstsendto
)
last_server
=
daemon
->
servers
;
break
;
}
if
(
last_server
==
firstsendto
)
break
;
/* server for wrong domain */
}
if
(
type
!=
(
last_server
->
flags
&
SERV_TYPE
)
||
(
type
==
SERV_HAS_DOMAIN
&&
!
hostname_isequal
(
domain
,
last_server
->
domain
))
||
if
(
type
!=
(
last_server
->
flags
&
SERV_TYPE
)
||
(
last_server
->
flags
&
(
SERV_LITERAL_ADDRESS
|
SERV_LOOP
)))
last_server
->
flags
&
SERV_HAS_DOMAIN
||
continue
;
last_server
->
flags
&
SERV_LOOP
)
continue
;
if
(
last_server
->
tcpfd
==
-
1
)
{
serv
=
last_server
;
if
((
last_server
->
tcpfd
=
socket
(
last_server
->
addr
.
sa
.
sa_family
,
SOCK_STREAM
,
0
))
==
-
1
)
ret
=
tcp_conn_serv
(
serv
,
now
,
packet
,
size
,
&
peer_addr
);
continue
;
/* something wrong with tcp connect/read/write */
if
(
ret
<=
0
)
#ifdef HAVE_CONNTRACK
continue
;
/* Copy connection mark of incoming query to outgoing connection. */
if
(
option_bool
(
OPT_CONNTRACK
))
break
;
{
}
unsigned
int
mark
;
}
struct
all_addr
local
;
#ifdef HAVE_IPV6
if
(
local_addr
->
sa
.
sa_family
==
AF_INET6
)
local
.
addr
.
addr6
=
local_addr
->
in6
.
sin6_addr
;
else
#endif
local
.
addr
.
addr4
=
local_addr
->
in
.
sin_addr
;
if
(
get_incoming_mark
(
&
peer_addr
,
&
local
,
1
,
&
mark
))
setsockopt
(
last_server
->
tcpfd
,
SOL_SOCKET
,
SO_MARK
,
&
mark
,
sizeof
(
unsigned
int
));
}
#endif
if
((
!
local_bind
(
last_server
->
tcpfd
,
&
last_server
->
source_addr
,
last_server
->
interface
,
1
)
||
connect
(
last_server
->
tcpfd
,
&
last_server
->
addr
.
sa
,
sa_len
(
&
last_server
->
addr
))
==
-
1
))
{
close
(
last_server
->
tcpfd
);
last_server
->
tcpfd
=
-
1
;
continue
;
}
#ifdef HAVE_DNSSEC
if
(
option_bool
(
OPT_DNSSEC_VALID
))
{
size_t
new_size
=
add_do_bit
(
header
,
size
,
((
char
*
)
header
)
+
65536
);
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
this allows it to select auth servers when one is returning bad data. */
if
(
option_bool
(
OPT_DNSSEC_DEBUG
))
header
->
hb4
|=
HB4_CD
;
if
(
size
!=
new_size
)
added_pheader
=
1
;
size
=
new_size
;
}
#endif
}
*
length
=
htons
(
size
);
/* get query name again for logging - may have been overwritten */
if
(
!
(
gotname
=
extract_request
(
header
,
(
unsigned
int
)
size
,
daemon
->
namebuff
,
&
qtype
)))
strcpy
(
daemon
->
namebuff
,
"query"
);
if
(
!
read_write
(
last_server
->
tcpfd
,
packet
,
size
+
sizeof
(
u16
),
0
)
||
!
read_write
(
last_server
->
tcpfd
,
&
c1
,
1
,
1
)
||
!
read_write
(
last_server
->
tcpfd
,
&
c2
,
1
,
1
)
||
!
read_write
(
last_server
->
tcpfd
,
payload
,
(
c1
<<
8
)
|
c2
,
1
))
{
close
(
last_server
->
tcpfd
);
last_server
->
tcpfd
=
-
1
;
continue
;
}
m
=
(
c1
<<
8
)
|
c2
;
if
(
last_server
->
addr
.
sa
.
sa_family
==
AF_INET
)
log_query
(
F_SERVER
|
F_IPV4
|
F_FORWARD
,
daemon
->
namebuff
,
(
struct
all_addr
*
)
&
last_server
->
addr
.
in
.
sin_addr
,
NULL
);
#ifdef HAVE_IPV6
else
log_query
(
F_SERVER
|
F_IPV6
|
F_FORWARD
,
daemon
->
namebuff
,
(
struct
all_addr
*
)
&
last_server
->
addr
.
in6
.
sin6_addr
,
NULL
);
#endif
#ifdef HAVE_DNSSEC
if
(
option_bool
(
OPT_DNSSEC_VALID
)
&&
!
checking_disabled
)
{
int
keycount
=
DNSSEC_WORK
;
/* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
int
status
=
tcp_key_recurse
(
now
,
STAT_TRUNCATED
,
header
,
m
,
0
,
daemon
->
namebuff
,
daemon
->
keyname
,
last_server
,
&
keycount
);
char
*
result
;
if
(
keycount
==
0
)
result
=
"ABANDONED"
;
else
result
=
(
status
==
STAT_SECURE
?
"SECURE"
:
(
status
==
STAT_INSECURE
?
"INSECURE"
:
"BOGUS"
));
log_query
(
F_KEYTAG
|
F_SECSTAT
,
"result"
,
NULL
,
result
);
if
(
status
==
STAT_BOGUS
)
no_cache_dnssec
=
1
;
if
(
status
==
STAT_SECURE
)
cache_secure
=
1
;
}
#endif
/* restore CD bit to the value in the query */
if
(
ret
>
0
)
if
(
checking_disabled
)
m
=
process_reply
(
header
,
now
,
serv
,
ret
,
header
->
hb4
|=
HB4_CD
;
option_bool
(
OPT_NO_REBIND
)
&&
!
norebind
,
else
no_cache_dnssec
,
cache_secure
,
ad_question
,
header
->
hb4
&=
~
HB4_CD
;
do_bit
,
added_pheader
,
check_subnet
,
&
peer_addr
);
/* There's no point in updating the cache, since this process will exit and
lose the information after a few queries. We make this call for the alias and
bogus-nxdomain side-effects. */
/* If the crc of the question section doesn't match the crc we sent, then
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
#ifdef HAVE_DNSSEC
newhash
=
hash_questions
(
header
,
(
unsigned
int
)
m
,
daemon
->
namebuff
);
if
(
!
newhash
||
memcmp
(
hash
,
newhash
,
HASH_SIZE
)
!=
0
)
{
m
=
0
;
break
;
}
#else
if
(
crc
!=
questions_crc
(
header
,
(
unsigned
int
)
m
,
daemon
->
namebuff
))
{
m
=
0
;
break
;
}
#endif
m
=
process_reply
(
header
,
now
,
last_server
,
(
unsigned
int
)
m
,
option_bool
(
OPT_NO_REBIND
)
&&
!
norebind
,
no_cache_dnssec
,
cache_secure
,
ad_question
,
do_bit
,
added_pheader
,
check_subnet
,
&
peer_addr
);
break
;
}
}
/* In case of local answer or no connections made. */
/* In case of local answer or no connections made. */
if
(
m
==
0
)
if
(
m
==
0
)
m
=
setup_reply
(
header
,
(
unsigned
int
)
size
,
addrp
,
flags
,
daemon
->
local_ttl
);
m
=
setup_reply
(
header
,
(
unsigned
int
)
size
,
addrp
,
flags
,
daemon
->
local_ttl
);
}
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment