blob: ee31d4ad2160ba37379e65b7580945acfa30338b [file] [log] [blame]
/*
*
* Copyright (c) 2013-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file implements a process to effect a functional test for
* LwIP's Domain Name Service (DNS) interface.
*
*/
#include <string.h>
#include "ToolCommon.h"
#include <lwip/dns.h>
#define TOOL_NAME "TestDNS"
// TODO BUG: TestDNS is never built.
static bool HandleNonOptionArgs(const char *progName, int argc, char *argv[]);
static void DriveSend();
static void HandleConnectionComplete(TCPEndPoint *ep, INET_ERROR conErr);
static void HandleConnectionReceived(TCPEndPoint *listeningEP, TCPEndPoint *conEP, IPAddress peerAddr, uint16_t peerPort);
static void HandleConnectionClosed(TCPEndPoint *ep, INET_ERROR err);
static void HandleDataSent(TCPEndPoint *ep, uint16_t len);
static void HandleDataReceived(TCPEndPoint *ep, PacketBuffer *data);
static void HandleAcceptError(TCPEndPoint *endPoint, INET_ERROR err);
static void HandleUDPMessageReceived(UDPEndPoint *endPoint, PacketBuffer *msg, IPAddress senderAddr, uint16_t senderPort);
static void HandleUDPReceiveError(UDPEndPoint *endPoint, INET_ERROR err, IPAddress senderAddr, uint16_t senderPort);
static PacketBuffer *MakeDataBuffer(int32_t len);
// Globals
ip_addr_t ipaddrs[DNS_MAX_ADDRS_PER_NAME];
uint8_t numipaddrs = DNS_MAX_ADDRS_PER_NAME;
bool Listen = false;
IPAddress DestAddr = IPAddress::Any;
uint64_t LastSendTime = 0;
int32_t MaxSendCount = -1;
int32_t SendInterval = 1000000;
int32_t SendLength = 3200;
int32_t MaxSendLength = -1;
int32_t MinRcvLength = 0;
int32_t MaxRcvLength = -1;
int32_t TotalSendLength = 0;
int32_t TotalRcvLength = 0;
bool UseTCP = false;
TCPEndPoint *ConnectionEP = NULL;
TCPEndPoint *ListenEP = NULL;
UDPEndPoint *UDPEP = NULL;
static const char *hostname = NULL;
static const char *DNSServerAddr = NULL;
static HelpOptions gHelpOptions(
TOOL_NAME,
"Usage: " TOOL_NAME " [<options...>] <hostname> <dns-server-address>\n",
WEAVE_VERSION_STRING "\n" WEAVE_TOOL_COPYRIGHT
);
static OptionSet *gToolOptionSets[] =
{
&gNetworkOptions,
&gFaultInjectionOptions,
&gHelpOptions,
NULL
};
static void
found_multi(const char *name, ip_addr_t *ipaddrs, u8_t numipaddrs, void *callback_arg)
{
int i;
printf("\tfound_multi response\n");
printf("\tName: %s\n", name);
printf("\tnumipaddrs: %d (DNS_MAX_ADDRS_PER_NAME: %d)\n", numipaddrs, DNS_MAX_ADDRS_PER_NAME);
for (i = 0; i < numipaddrs; ++i)
{
char addrStr[64];
IPAddress::FromIPv4(ipaddrs[i]).ToString(addrStr, sizeof(addrStr));
printf("\t(%d) IPv4: %s\n", i, addrStr);
}
Done = true;
}
int main(int argc, char *argv[])
{
unsigned int i;
SetSIGUSR1Handler();
if (argc == 1)
{
gHelpOptions.PrintBriefUsage(stderr);
exit(EXIT_FAILURE);
}
if (!ParseArgs(TOOL_NAME, argc, argv, gToolOptionSets, HandleNonOptionArgs))
{
exit(EXIT_FAILURE);
}
InitSystemLayer();
InitNetwork();
u8_t numdns = 1;
ip_addr_t dnsserver[1];
IPAddress DNSServerIPv4Addr;
IPAddress::FromString(DNSServerAddr, DNSServerIPv4Addr);
dnsserver[0] = DNSServerIPv4Addr.ToIPv4();
dns_setserver(numdns, dnsserver);
printf("\nStarted dns_gethostbyname_multi test...\n\n");
//Expected request / response
printf("Expected request / response #1\n");
printf("hn: %s, ips: %p, nips: %d, fm: %p, arg: %p\n", hostname, ipaddrs, numipaddrs, found_multi, NULL);
printf("ip[0]: %d, ip[1]: %d\n", ipaddrs[0], ipaddrs[1]);
err_t res = dns_gethostbyname_multi(hostname, ipaddrs, &numipaddrs, found_multi, NULL);
if (res == ERR_INPROGRESS)
{
printf("\tdns_gethostbyname_multi: %d (ERR_INPROGRESS)\n", res);
}
else
{
printf("\tdns_gethostbyname_multi: %d (expected -5: ERR_INPROGRESS)\n", res);
}
/*
//Cancel request / response
printf("Cancel request / response #1\n");
dns_cancel((dns_found_callbackX)found_multi, NULL);
//Expected request / response
printf("Expected request / response #2\n");
res = dns_gethostbyname_multi(hostname, ipaddrs, &numipaddrs, found_multi, NULL);
if (res == ERR_INPROGRESS)
{
printf("\tdns_gethostbyname_multi: %d (ERR_INPROGRESS)\n", res);
}
else
{
printf("\tdns_gethostbyname_multi: %d (expected -5: ERR_INPROGRESS)\n", res);
}
*/
while (!Done)
{
struct timeval sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_usec = 10000;
ServiceNetwork(sleepTime);
}
//Expected cached response
printf("Expected cached response #1\n");
numipaddrs = DNS_MAX_ADDRS_PER_NAME;
printf("hn: %s, ips: %p, nips: %d, fm: %p, arg: %p\n", hostname, ipaddrs, numipaddrs, found_multi, NULL);
printf("ip[0]: %d, ip[1]: %d\n", ipaddrs[0], ipaddrs[1]);
res = dns_gethostbyname_multi(hostname, ipaddrs, &numipaddrs, found_multi, NULL);
if (res == ERR_OK)
{
printf("\tdns_gethostbyname_multi: %d (ERR_OK)\n", res);
printf("\tlocal DNS cache response\n");
printf("\tName: %s\n", hostname);
printf("\tnumipaddrs: %d\n", numipaddrs);
for (i = 0; i < numipaddrs; ++i)
{
char addrStr[64];
IPAddress::FromIPv4(ipaddrs[i]).ToString(addrStr, sizeof(addrStr));
printf("\t(%d) IPv4: %s\n", i, addrStr);
}
}
else
{
printf("\tdns_gethostbyname_multi: %d (expected : ERR_OK)\n", res);
}
//Expected cached response
printf("Expected cached response #2\n");
numipaddrs = DNS_MAX_ADDRS_PER_NAME-1;
printf("hn: %s, ips: %p, nips: %d, fm: %p, arg: %p\n", hostname, ipaddrs, numipaddrs, found_multi, NULL);
printf("ip[0]: %d, ip[1]: %d\n", ipaddrs[0], ipaddrs[1]);
res = dns_gethostbyname_multi(hostname, ipaddrs, &numipaddrs, found_multi, NULL);
if (res == ERR_OK)
{
printf("\tdns_gethostbyname_multi: %d (ERR_OK)\n", res);
printf("\tlocal DNS cache response\n");
printf("\tName: %s\n", hostname);
printf("\tnumipaddrs: %d\n", numipaddrs);
for (i = 0; i < numipaddrs; ++i)
{
char addrStr[64];
IPAddress::FromIPv4(ipaddrs[i]).ToString(addrStr, sizeof(addrStr));
printf("\t(%d) IPv4: %s\n", i, addrStr);
}
}
else
{
printf("\tdns_gethostbyname_multi: %d (expected : ERR_OK)\n", res);
}
//Expected cached response
printf("Expected cached response #3\n");
numipaddrs = 0;
printf("hn: %s, ips: %p, nips: %d, fm: %p, arg: %p\n", hostname, ipaddrs, numipaddrs, found_multi, NULL);
printf("ip[0]: %d, ip[1]: %d\n", ipaddrs[0], ipaddrs[1]);
res = dns_gethostbyname_multi(hostname, ipaddrs, &numipaddrs, found_multi, NULL);
if (res == ERR_OK)
{
printf("\tdns_gethostbyname_multi: %d (ERR_OK)\n", res);
printf("\tlocal DNS cache response\n");
printf("\tName: %s\n", hostname);
printf("\tnumipaddrs: %d\n", numipaddrs);
for (i = 0; i < numipaddrs; ++i)
{
char addrStr[64];
IPAddress::FromIPv4(ipaddrs[i]).ToString(addrStr, sizeof(addrStr));
printf("\t(%d) IPv4: %s\n", i, addrStr);
}
}
else
{
printf("\tdns_gethostbyname_multi: %d (expected : ERR_OK)\n", res);
}
return 0;
}
bool HandleNonOptionArgs(const char *progName, int argc, char *argv[])
{
if (argc < 2)
{
printf("TestDNS: Missing %s argument\n", argc == 0 ? "<hostname>" : "<dns-server-address>");
return false;
}
if (argc > 2)
{
printf("Unexpected argument: %s\n", argv[1]);
}
hostname = argv[0];
DNSServerAddr = argv[1];
return true;
}
void StartTest()
{
INET_ERROR err;
if (!UseTCP)
{
err = Inet.NewUDPEndPoint(&UDPEP);
FAIL_ERROR(err, "InetLayer::NewUDPEndPoint failed");
UDPEP->OnMessageReceived = HandleUDPMessageReceived;
UDPEP->OnReceiveError = HandleUDPReceiveError;
}
if (Listen)
{
if (UseTCP)
{
err = Inet.NewTCPEndPoint(&ListenEP);
FAIL_ERROR(err, "InetLayer::NewTCPEndPoint failed");
ListenEP->OnConnectionReceived = HandleConnectionReceived;
ListenEP->OnAcceptError = HandleAcceptError;
err = ListenEP->Bind(kIPAddressType_IPv6, IPAddress::Any, 4242, true);
FAIL_ERROR(err, "TCPEndPoint::Bind failed");
err = ListenEP->Listen(1);
FAIL_ERROR(err, "TCPEndPoint::Listen failed");
}
else
{
err = UDPEP->Bind(kIPAddressType_IPv6, IPAddress::Any, 4242);
FAIL_ERROR(err, "UDPEndPoint::Bind failed");
err = UDPEP->Listen();
FAIL_ERROR(err, "UDPEndPoint::Listen failed");
}
printf("Listening...\n");
}
else
{
err = Inet.NewTCPEndPoint(&ConnectionEP);
FAIL_ERROR(err, "InetLayer::NewTCPEndPoint failed");
ConnectionEP->OnConnectComplete = HandleConnectionComplete;
ConnectionEP->OnConnectionClosed = HandleConnectionClosed;
ConnectionEP->OnDataSent = HandleDataSent;
ConnectionEP->OnDataReceived = HandleDataReceived;
err = ConnectionEP->Connect(DestAddr, 4242);
FAIL_ERROR(err, "TCPEndPoint::Connect failed");
}
}
void DriveSend()
{
if (TotalSendLength == MaxSendLength)
return;
bool isTimeToSend = (Now() >= LastSendTime + SendInterval);
if (!isTimeToSend)
return;
if (UseTCP)
{
if (ConnectionEP == NULL)
return;
if (ConnectionEP->PendingSendLength() > 0)
return;
int32_t sendLen = SendLength;
if (MaxSendLength != -1)
{
int32_t remainingLen = MaxSendLength - TotalSendLength;
if (sendLen > remainingLen)
sendLen = remainingLen;
}
INET_ERROR err;
PacketBuffer *buf = MakeDataBuffer(sendLen);
if (buf == NULL)
{
printf("Failed to allocate PacketBuffer\n");
LastSendTime = Now();
return;
}
LastSendTime = Now();
err = ConnectionEP->Send(buf);
FAIL_ERROR(err, "TCPEndPoint::Send failed");
TotalSendLength += sendLen;
if (MaxSendLength != -1 && TotalSendLength == MaxSendLength && !Listen)
{
printf("Closing connection\n");
err = ConnectionEP->Close();
FAIL_ERROR(err, "TCPEndPoint::Close failed");
printf("Freeing end point\n");
ConnectionEP->Free();
ConnectionEP = NULL;
}
}
else if (DestAddr != IPAddress::Any)
{
int32_t sendLen = SendLength;
if (MaxSendLength != -1)
{
int32_t remainingLen = MaxSendLength - TotalSendLength;
if (sendLen > remainingLen)
sendLen = remainingLen;
}
INET_ERROR err;
PacketBuffer *buf = MakeDataBuffer(sendLen);
if (buf == NULL)
{
printf("Failed to allocate PacketBuffer\n");
LastSendTime = Now();
return;
}
LastSendTime = Now();
err = UDPEP->SendTo(DestAddr, 4242, buf);
FAIL_ERROR(err, "UDPEndPoint::SendTo failed");
TotalSendLength += sendLen;
}
}
void HandleConnectionComplete(TCPEndPoint *ep, INET_ERROR conErr)
{
INET_ERROR err;
IPAddress peerAddr;
uint16_t peerPort;
if (conErr == WEAVE_NO_ERROR)
{
err = ep->GetPeerInfo(&peerAddr, &peerPort);
FAIL_ERROR(err, "TCPEndPoint::GetPeerInfo failed");
char peerAddrStr[64];
peerAddr.ToString(peerAddrStr, sizeof(peerAddrStr));
printf("Connection established to %s:%ld\n", peerAddrStr, (long)peerPort);
}
else
printf("Connection FAILED: %s\n", ErrorStr(conErr));
}
void HandleConnectionReceived(TCPEndPoint *listeningEP, TCPEndPoint *conEP, IPAddress peerAddr, uint16_t peerPort)
{
char peerAddrStr[64];
peerAddr.ToString(peerAddrStr, sizeof(peerAddrStr));
if (ConnectionEP == NULL)
{
printf("Accepted connection from %s, port %ld\n", peerAddrStr, (long)peerPort);
conEP->OnConnectComplete = HandleConnectionComplete;
conEP->OnConnectionClosed = HandleConnectionClosed;
conEP->OnDataSent = HandleDataSent;
conEP->OnDataReceived = HandleDataReceived;
ConnectionEP = conEP;
}
else
printf("Rejected connection from %s, port %ld\n", peerAddrStr, (long)peerPort);
}
void HandleConnectionClosed(TCPEndPoint *ep, INET_ERROR err)
{
if (err == WEAVE_NO_ERROR)
printf("Connection closed\n");
else
printf("Connection closed with error: %s\n", ErrorStr(err));
printf("Freeing end point\n");
ep->Free();
if (ep == ConnectionEP)
ConnectionEP = NULL;
}
void HandleDataSent(TCPEndPoint *ep, uint16_t len)
{
printf("Data sent: %ld\n", (long)len);
if (ep->State == TCPEndPoint::kState_Closed)
{
printf("Freeing end point\n");
ep->Free();
}
else
DriveSend();
}
void HandleDataReceived(TCPEndPoint *ep, PacketBuffer *data)
{
INET_ERROR err;
if (data->TotalLength() < MinRcvLength && ep->State == TCPEndPoint::kState_Connected)
{
err = ep->PutBackReceivedData(data);
FAIL_ERROR(err, "TCPEndPoint::PutBackReceivedData failed");
return;
}
for (PacketBuffer *buf = data; buf; buf = buf->Next())
{
printf("Data received (%ld bytes)\n", (long)buf->DataLength());
DumpMemory(buf->Start(), buf->DataLength(), " ", 16);
uint8_t *p = buf->Start();
for (uint16_t i = 0; i < buf->DataLength(); i++, p++, TotalRcvLength++)
if (*p != (uint8_t)TotalRcvLength)
{
printf("Bad data value, offset %d\n", (int)i);
exit(-1);
}
}
ep->AckReceive(data->TotalLength());
PacketBuffer::Free(data);
if (MaxRcvLength != -1 && TotalRcvLength >= MaxRcvLength)
{
printf("Closing connection\n");
err = ep->Close();
FAIL_ERROR(err, "TCPEndPoint::Close failed");
printf("Freeing end point\n");
ep->Free();
if (ep == ConnectionEP)
ConnectionEP = NULL;
}
}
void HandleAcceptError(TCPEndPoint *endPoint, INET_ERROR err)
{
printf("Accept error: %s\n", ErrorStr(err));
}
void HandleUDPMessageReceived(UDPEndPoint *endPoint, PacketBuffer *msg, IPAddress senderAddr, uint16_t senderPort)
{
char senderAddrStr[64];
senderAddr.ToString(senderAddrStr, sizeof(senderAddrStr));
printf("UDP message received from %s, port %ld (%ld bytes)\n", senderAddrStr, (long)senderPort, (long)msg->DataLength());
DumpMemory(msg->Start(), msg->DataLength(), " ", 16);
PacketBuffer::Free(msg);
}
void HandleUDPReceiveError(UDPEndPoint *endPoint, INET_ERROR err, IPAddress senderAddr, uint16_t senderPort)
{
char senderAddrStr[64];
senderAddr.ToString(senderAddrStr, sizeof(senderAddrStr));
printf("UDP receive error (%s, port %ld): %s\n", senderAddrStr, (long)senderPort, ErrorStr(err));
}
PacketBuffer *MakeDataBuffer(int32_t desiredLen)
{
PacketBuffer *buf;
buf = PacketBuffer::New();
if (buf == NULL)
return NULL;
if (desiredLen > buf->MaxDataLength())
desiredLen = buf->MaxDataLength();
char *p = (char *)buf->Start();
for (uint16_t i = 0; i < desiredLen; i++, p++)
*p = (uint8_t)(TotalSendLength + i);
buf->SetDataLength(desiredLen);
return buf;
}