summaryrefslogtreecommitdiff
path: root/tests/SOCK_Connector_Test.cpp
blob: 229bac706fd3a3382fe5b9b8b5b5b39d0a0114ab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
// $Id$

// ==========================================================================
//
// = LIBRARY
//    tests
//
// = FILENAME
//    SOCK_Connector_Test.cpp
//
// = DESCRIPTION
//     This is a test of ACE_SOCK_Connector, focusing on failure cases more
//     than on success cases.
//
// = AUTHOR
//    Steve Huston <shuston@riverace.com>
//
// ==========================================================================

#include "test_config.h"
#include "ace/OS_NS_string.h"
#include "ace/INET_Addr.h"
#include "ace/SOCK_Connector.h"
#include "ace/Time_Value.h"
#include "ace/SOCK_Stream.h"
#include "ace/OS_NS_sys_utsname.h"
#include "ace/OS_NS_netdb.h"

ACE_RCSID(tests, SOCK_Connector_Test, "$Id$")

// Host candidate list
struct Host_Candidate
{
  ACE_TCHAR host_name[MAXHOSTNAMELEN];
};

const int MAX_CANDIDATES = 50;
Host_Candidate candidate[MAX_CANDIDATES];

#if !defined (ACE_LACKS_GETHOSTENT)
// Determine if a host exists, is reachable, and is up.  Attempt a
// blocking connection to it; if it succeeds, then the host exists, is
// reachable, and is up.

static u_int
host_is_up (ACE_TCHAR hostname[])
{
  ACE_SOCK_Connector con;
  ACE_SOCK_Stream sock;

  // The ACE_INET_Addr construction causes gethostbyname_r to be
  // called, so we need to copy the hostname.
  ACE_TCHAR test_host[MAXHOSTNAMELEN];
  ACE_OS::strcpy (test_host, hostname);

  ACE_INET_Addr another_host ((u_short) 7, test_host);
  ACE_Time_Value timeout_value (5);
  const int status = con.connect (sock,
                                  another_host,
                                  &timeout_value);
  sock.close ();
  return status == 0  ?  1  :  0;
}
#endif /* ! ACE_LACKS_GETHOSTENT */

// The original problem this program tested for was incorrectly saying
// a non-blocking connect completed successfully when it didn't.  The
// test doesn't always work when done to localhost
// (platform-dependant) so we look around for another host - any other
// one will do.

static void
find_another_host (ACE_TCHAR other_host[])
{
  static ACE_TCHAR cached_other_host[MAXHOSTNAMELEN] = {'\0'};

  if (cached_other_host[0] == '\0')
    {

      ACE_OS::strcpy (other_host,
                      ACE_DEFAULT_SERVER_HOST); // If all else fails

#if !defined (ACE_LACKS_GETHOSTENT)
      // These gethost-type things don't work everywhere.
      struct hostent *h;
      ACE_utsname un;

      ACE_OS::uname (&un);

      h = ACE_OS::gethostbyname (un.nodename);

      if (h == 0)
        ACE_OS::strcpy (other_host, ACE_LOCALHOST);
      else
        // Use me if can't find another
        ACE_OS::strcpy (other_host, ACE_TEXT_TO_TCHAR_IN (h->h_name));

      // @@ We really need to add wrappers for these hostent methods.

      // AIX 4.3 has problems with DNS usage when sethostent(1) is
      // called - further DNS lookups don't work at all.
#if defined (ACE_AIX_MINOR_VERS) && (ACE_AIX_MINOR_VERS == 3)
      sethostent (0);
#else
      sethostent (1);
#endif /* (ACE_AIX_MINOR_VERS) && (ACE_AIX_MINOR_VERS == 3) */

      int candidate_count = 0;

      // Accumulate candidates first.  There is some interaction on
      // Linux systems between <gethostent> and <gethostbyname_r>
      // (called by ACE_INET_Addr in host_is_up) This otherwise causes
      // an infinite loop on Linux --mas 03-08-2001
      while ((h = gethostent ()) != 0)
        {
          if (ACE_OS::strcmp (h->h_name,
                              ACE_TEXT_TO_CHAR_IN (ACE_DEFAULT_SERVER_HOST)) == 0)
            continue;
          // AIX just _has_ to be different
          if (ACE_OS::strcmp (h->h_name, "loopback") == 0)
            continue;

          // If not me.
          if (ACE_OS::strcmp
                (h->h_name, ACE_TEXT_TO_CHAR_IN (other_host)) != 0
              && ACE_OS::strcmp (h->h_name, un.nodename) != 0)
            {
               ACE_OS::strcpy (candidate[candidate_count].host_name,
                               ACE_TEXT_TO_TCHAR_IN (h->h_name));
               if (++candidate_count >= MAX_CANDIDATES)
                 break;
            }
        }

      // Now try to connect to candidates
      for (int i = 0; i < candidate_count; i++)
        if (host_is_up (candidate[i].host_name))
          {
            ACE_OS::strcpy (other_host, candidate[i].host_name);
            break;
          }

      endhostent ();
#endif /* ! ACE_LACKS_GETHOSTENT */

      ACE_OS::strcpy (cached_other_host, other_host);
    }
  else
    ACE_OS::strcpy (other_host, cached_other_host);
}

static int
fail_no_listener_nonblocking (void)
{
  ACE_TCHAR test_host[MAXHOSTNAMELEN];
  int status;
  ACE_INET_Addr nobody_home;
  ACE_SOCK_Connector con;
  ACE_SOCK_Stream sock;
  ACE_Time_Value nonblock (0, 0);

  find_another_host (test_host);
  ACE_DEBUG ((LM_DEBUG,
              ACE_TEXT ("Testing to host \"%s\"\n"),
              test_host));

  if (nobody_home.set ((u_short) 42000, test_host) == -1)
    {
      ACE_ERROR ((LM_ERROR,
                  ACE_TEXT ("Host lookup for %s %p\n"),
                  test_host,
                  ACE_TEXT ("failed")));
      return -1;
    }

  status = con.connect (sock, nobody_home, &nonblock);

  // Need a port that will fail.
  ACE_ASSERT (status == -1);

  // On some systems, a failed connect to localhost will return
  // ECONNREFUSED or ENETUNREACH directly, instead of
  // EWOULDBLOCK. That is also fine.

  if (errno == EWOULDBLOCK || errno == ECONNREFUSED || errno == ENETUNREACH)
    {
      if (sock.get_handle () != ACE_INVALID_HANDLE)
        status = con.complete (sock);

      if (status != -1)
        {
          ACE_ERROR ((LM_ERROR,
                      ACE_TEXT ("Connect which should fail didn't\n")));
          status = -1;
        }
      else
        {
          ACE_DEBUG ((LM_DEBUG,
                      ACE_TEXT ("%p\n"),
                      ACE_TEXT ("Proper fail")));
          status = 0;
        }
    }
  else
    {
      ACE_DEBUG ((LM_WARNING,
                  ACE_TEXT ("Test not executed fully; ")
                  ACE_TEXT ("expected EWOULDBLOCK, %p (%d)\n"),
                  ACE_TEXT ("not"), errno));
      status = -1;
    }

  // Just in case.
  sock.close ();

  return status;
}


// This test tries to hit a port that's listening.  Echo (7) is pretty
// popular.  Just in case, though, it won't report a failure if it
// gets "refused" (no listener) since the real fixed bug this is
// testing is a returned error of EWOULDBLOCK when the connect really
// did work.  That was a side-affect of how
// <ACE::handle_timed_complete> does checks on some systems.

static int
succeed_nonblocking (void)
{
  ACE_TCHAR test_host[MAXHOSTNAMELEN];
  int status;
  ACE_INET_Addr echo_server;
  ACE_SOCK_Connector con;
  ACE_SOCK_Stream sock;
  ACE_Time_Value nonblock (0, 0);
  u_short test_port = 7;          // Echo

  find_another_host (test_host);
  if (ACE_OS::strcmp (ACE_TEXT ("localhost"), test_host) == 0)
    {
#if defined (ACE_WIN32)
      test_port = 80;        // Echo not available on Win32; try web server
#endif /* ACE_WIN32 */
    }
  ACE_DEBUG ((LM_DEBUG,
              ACE_TEXT ("Testing to host \"%s\", port %d\n"),
              test_host, test_port));
  if (echo_server.set (test_port, test_host) == -1)
    {
      ACE_ERROR ((LM_ERROR,
                  ACE_TEXT ("Host lookup for %s %p\n"),
                  test_host,
                  ACE_TEXT ("failed")));
      return -1;
    }
  status = con.connect (sock, echo_server, &nonblock);

  // Need to test the call to 'complete' really.
  if (status == 0 || (status == -1 && errno != EWOULDBLOCK))
    {
      ACE_DEBUG((LM_WARNING,
                 ACE_TEXT ("Immediate success/fail; test not completed\n")));
      status = 0;
    }
  else
    {
      if (sock.get_handle () != ACE_INVALID_HANDLE)
        status = con.complete (sock);

      if (status == -1)
        {
          // Reset the status _before_ doing the printout, in case the
          // printout overwrites errno.
          if (errno == ECONNREFUSED)
            {
              status = 0;
              ACE_DEBUG ((LM_DEBUG,
                          ACE_TEXT ("Should succeed, but refused: ok\n")));
            }
          else
            {
              ACE_ERROR ((LM_ERROR,
                          ACE_TEXT("%p\n"),
                          ACE_TEXT("connect should succeed, but")));
            }
        }
      else
        ACE_DEBUG((LM_DEBUG,
                   ACE_TEXT("Connect which should succeed, did\n")));
    }

  // Just in case.
  sock.close ();

  return status;
}

int
run_main (int, ACE_TCHAR *[])
{
  ACE_START_TEST (ACE_TEXT ("SOCK_Connector_Test"));

  int status = 0;

  if (fail_no_listener_nonblocking () == -1)
    status = 1;

  if (succeed_nonblocking () == -1)
    status = 1;

  ACE_END_TEST;
  return status;
}