/*
* Copyright (c) 2002-2005 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: sndrcv.c,v 1.20 2006/10/05 01:42:23 ca Exp $")
#include "sm/types.h"
#include "sm/ctype.h"
#include "sm/dns.h"
#include "sm/dnstsk.h"
#include "sm/dns-int.h"
#include "log.h"
#if MTA_LIBDNS_DEBUG
# include "sm/io.h"
#endif
/*
** See RFC 1035 about the format of messages.
4.2.2. TCP usage
Messages sent over TCP connections use server port 53 (decimal). The
message is prefixed with a two byte length field which gives the message
length, excluding the two byte length field. This length field allows
the low-level processing to assemble a complete message before beginning
to parse it.
*/
/*
** DNS_SEND -- send a query to a nameserver
**
** Parameters:
** dns_tsk -- DNS task (includes query)
**
** Return value:
** usual sm_error code
*/
sm_ret_T
dns_send(dns_tsk_P dns_tsk)
{
ssize_t r;
SM_ASSERT(dns_tsk != NULL);
/*
** XXX shouldn't this switch "automagically", i.e., when a
** truncation error occurs? But then we need a different socket...
*/
if (SM_IS_FLAG(dns_tsk->dnstsk_flags, DNS_TSK_FL_USETCP))
{
u_short len;
struct iovec io[2];
len = htons(sm_str_getlen(dns_tsk->dnstsk_wr));
io[0].iov_base = (void *) &len;
io[0].iov_len = sizeof(len);
io[1].iov_base = (void *) sm_str_data(dns_tsk->dnstsk_wr);
io[1].iov_len = sm_str_getlen(dns_tsk->dnstsk_wr);
r = writev(dns_tsk->dnstsk_fd, io, 2);
MTA_LIBDNS_DBG_DPRINTF((smioerr, "dns_send: writev()=%d\n", r));
}
else if (SM_IS_FLAG(dns_tsk->dnstsk_flags, DNS_TSK_FL_CONNECTUDP))
{
r = write(dns_tsk->dnstsk_fd,
(const void *) sm_str_data(dns_tsk->dnstsk_wr),
sm_str_getlen(dns_tsk->dnstsk_wr));
MTA_LIBDNS_DBG_DPRINTF((smioerr, "dns_send: write()=%d\n", r));
}
else
{
r = sendto(dns_tsk->dnstsk_fd,
(const void *) sm_str_data(dns_tsk->dnstsk_wr),
sm_str_getlen(dns_tsk->dnstsk_wr), 0,
(sockaddr_P) &dns_tsk->dnstsk_sin,
sizeof(sockaddr_in_T));
MTA_LIBDNS_DBG_DPRINTF((smioerr, "dns_send: sendto(%d)=%d\n",
sm_str_getlen(dns_tsk->dnstsk_wr), r));
}
if (r == -1)
return sm_error_perm(SM_EM_DNS, errno == 0 ? EIO : errno);
else if (r < sm_str_getlen(dns_tsk->dnstsk_wr))
return sm_error_temp(SM_EM_DNS, errno == 0 ? EIO : errno);
return SM_SUCCESS;
}
/*
** DNS_RECEIVE -- receive a reply from the nameserver
**
** Parameters:
** dns_tsk -- DNS task (stores reply)
**
** Return value:
** >0 Bytes returned;
** ==0 if the message timed out
** <0: usual sm_error code
*/
sm_ret_T
dns_receive(dns_tsk_P dns_tsk)
{
sm_ret_T ret;
ssize_t r;
socklen_T fromlen;
sockaddr_in_T from;
SM_ASSERT(dns_tsk != NULL);
fromlen = sizeof(from);
sm_memzero((char *)&from, sizeof(from));
if (SM_IS_FLAG(dns_tsk->dnstsk_flags, DNS_TSK_FL_USETCP))
{
u_short len;
int part;
uchar *where;
errno = 0;
r = read(dns_tsk->dnstsk_fd, (void *) &len, sizeof(len));
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"dns_receive: read(%d)=%d\n", len, r));
if (r == -1)
return sm_error_temp(SM_EM_DNS, errno);
else if (r < (int)sizeof(len))
{
sm_log_write(dns_tsk->dnstsk_mgr->dnsmgr_lctx,
LIBDNS_LCAT_RESOLVER, LIBDNS_LMOD_RESOLVER,
SM_LOG_ERROR, 4,
"sev=ERROR, func=dns_receive, where=1, status=nameserver_truncated, r=%d, len=%d, errno=%d, fd=%d\n",
r, sizeof(len), errno, dns_tsk->dnstsk_fd);
return sm_error_temp(SM_EM_DNS, EIO);
}
#if 0
if (!ipv4_addr_eq(dns_tsk->dnstsk_sin.sin_addr.s_addr,
from.sin_addr.s_addr))
{
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"read(): send_addr=%x, recv_addr=%x\n",
dns_tsk->dnstsk_sin.sin_addr.s_addr,
from.sin_addr.s_addr));
r = sm_error_temp(SM_EM_DNS, SM_E_IP_MISM);
}
#endif /* 0 */
len = ntohs(len);
ret = sm_str_space(dns_tsk->dnstsk_rd, len);
if (sm_is_err(ret))
return sm_error_temp(SM_EM_DNS, sm_error_value(ret));
SM_STR_SETLEN(dns_tsk->dnstsk_rd, len);
r = 0;
where = sm_str_data(dns_tsk->dnstsk_rd);
/* XXX Do NOT loop here... need to store state in dns_tsk! */
while (len > 0)
{
part = read(dns_tsk->dnstsk_fd, (void *) where, len);
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"dns_receive: 2 read(%d)=%d\n", len, part));
if (part == -1)
{
sm_log_write(dns_tsk->dnstsk_mgr->dnsmgr_lctx,
LIBDNS_LCAT_RESOLVER, LIBDNS_LMOD_RESOLVER,
SM_LOG_ERROR, 4,
"sev=ERROR, func=dns_receive, read=%s",
strerror(errno));
break;
}
else if (part == 0)
{
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"2 nameserver read(): truncated"));
return 0;
}
r += part;
len -= part;
where += part;
}
}
else
{
r = recvfrom(dns_tsk->dnstsk_fd,
(void *) sm_str_data(dns_tsk->dnstsk_rd),
sm_str_getsize(dns_tsk->dnstsk_rd), 0,
(sockaddr_P)&from, &fromlen);
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"dns_receive: recvfrom(%d)=%d\n",
sm_str_getsize(dns_tsk->dnstsk_rd), r));
if (r == -1)
{
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"nameserver recvfrom(): %s",
strerror(errno)));
if (r == EAGAIN)
r = 0;
else
r = sm_error_temp(SM_EM_DNS, r);
}
else if (r >= 0)
{
if (ipv4_addr_eq(dns_tsk->dnstsk_sin.sin_addr.s_addr,
from.sin_addr.s_addr))
{
/* addresses are equal */
SM_STR_SETLEN(dns_tsk->dnstsk_rd, r);
}
else
{
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"recvfrom(): send_addr=%x, recv_addr=%x\n",
dns_tsk->dnstsk_sin.sin_addr.s_addr,
from.sin_addr.s_addr));
r = sm_error_temp(SM_EM_DNS, SM_E_IP_MISM);
}
}
}
return r;
}
syntax highlighted by Code2HTML, v. 0.9.1